Skip to content

Allow custom decorators that use lifecycle hooks in AoT - these are our version of hooks, but we can't use them #30497

Closed

Description

@alshdavid

When writing a custom decorator, lifecycle methods you patch are removed in AoT because the compiler doesn't check that custom decorators are using them.

This results in an inability to create custom decorators with any meaningful component specific behaviour.

React uses hooks to help patch into setup/teardown of your functional components, decorators are Angulars form of hooks. It would be super useful to have this functionality in Angular.

Activity

  1. christopher-kiss commented on May 16, 2019

    @christopher-kiss

    @alshdavid,

    It looks like the onChanges life cycle works, but ngOnInit doesn't. Using a production build running with AoT, in the console I can see required and on changes, but not init. Switching to JIT development I correctly see the init messages.

    Would be great to have this fixed.

    export function RequiredInput<TValue>(defaultValue?: TValue) {
        return function<TInstance extends { [P in TKey]: TValue }, TKey extends keyof TInstance>(
            propertyClass: TInstance & Target,
            propertyKey: TKey & string
        ) {
            console.log('required');
          
            const originalInit = propertyClass.ngOnInit;
            const originalChanges = propertyClass.ngOnChanges;
    
            propertyClass.ngOnChanges = function(changes: SimpleChanges) {
                console.log('on changes');
                 if (originalChanges) {
                     originalChanges.apply(this, [changes]);
                 }
            };
    
            propertyClass.ngOnInit = function() {
                console.log('init');
                if (originalInit) {
                     originalInit.apply(this);
                }
            };
        };
    }
    

    Edit -
    As a workaround, I've had to resort to creating a class that implements these and extend this class.

    export abstract class RequiredClass implements OnInit, OnChanges {
        public ngOnInit() {}
        public ngOnChanges(_: SimpleChanges) {}
    }
    
    @Component({ ... })
    public TestComponent extends RequiredClass {}
    
  2. alshdavid commented on May 16, 2019

    @alshdavid
    Author

    If it's hard for the compiler to statically analyse a custom decorator to determine what lifecycle methods are being patched, perhaps a basic solution like using comments which target the compiler will be a simple temporary solution.

    eg

    // @AoT [ 'ngOnInit', 'onDestroy' ]
    export function RequiredInput<TValue>(defaultValue?: TValue) {
        ...
    }
  3. trotyl commented on May 16, 2019

    @trotyl
    Contributor

    Duplicate of #16023

  4. added this to the needsTriage milestone on May 16, 2019
  5. modified the milestones: needsTriage, Backlog on May 28, 2019
  6. alxhub commented on May 28, 2019

    @alxhub
    Member

    I have some good news!

    In Ivy, lifecycle hooks are purely a runtime concern. They're read off the prototype of the component as soon as the first instance is created, which means that if a decorator adds hooks to the prototype then Ivy will notice them.

  7. stupidawesome commented on May 29, 2019

    @stupidawesome

    @alxhub Should this be working in 8.0? I enabled Ivy and tried adding hooks with decorators using renderComponent() and platformBrowserDynamic().bootstrapModule() but it still doesn't work in AoT without adding an empty stub to the class definition.

    Should this code theoretically work?

    @Component()
    export class MyComponent {}
    
    MyComponent.prototype.ngOnInit = function () {
        console.log("init fired")
    }
  8. trotyl commented on May 29, 2019

    @trotyl
    Contributor

    @alxhub

    They're read off the prototype of the component as soon as the first instance is created

    This is not true, lifecycle hooks are determined in defineComponent(), which happen when the constructor is created, not the instance.

    lifecycle hooks are purely a runtime concern.

    However, decorators* are also purely a runtime concern, as explained in #16023 (comment), decorators runs after static properties, can be reproduce easily in TypeScript playground.

    When defineComponent() get called, all the decorators hasn't being executed yet, so no methods can be found in the prototype.

    The current decorator proposal has changed to compile-time semantics, but what implemented in TypeScript is still the obsolete stage-0 proposal.

  9. christopher-kiss commented on May 29, 2019

    @christopher-kiss

    @trotyl,

    So it looks like this is blocked until Typescript updates the decorator implementation? Which doesn't look like it's in a release plan yet: https://github.com/Microsoft/TypeScript/wiki/Roadmap#future

  10. 12 remaining items

  11. added a commit that references this issue on Jul 17, 2020
    14b4718
  12. angular-automatic-lock-bot commented on Aug 15, 2020

    @angular-automatic-lock-bot

    This issue has been automatically locked due to inactivity.
    Please file a new issue if you are encountering a similar or related problem.

    Read more about our automatic conversation locking policy.

    This action has been performed automatically by a bot.

  13. locked and limited conversation to collaborators on Aug 15, 2020
  14. added a commit that references this issue on Sep 5, 2020
    3ea86af
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: coreIssues related to the framework runtimefeatureLabel used to distinguish feature request from other issuesfreq1: low

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions