Skip to content

Angular compiler - custom decorators with ngOnDestroy does not work with AOT build #16023

Closed

Description

@ArielGueta

Versions

4.0.0

Repro steps

  1. Create a class decorator with debugger statement
export function Test() {
  return function ( constructor ) {
     debugger;
  }
}
  1. Use the class on a component
@Component({...})
@Test()
export class TestComponent {}
  1. Run the command ng build --aot --prod

The log given by the failure.

The debugger statement never called. The decorator doesn't work.

I also tried to create the decorator as npm package and compile the source file with the angular compiler ngc -p tsconfig.json but with no luck.

Desired functionality.

Custom decorators should be support out of the box by AOT.

Note:

It's not an angular-cli problem, the problem was also the same with angular2-webpack-starter.

Activity

  1. changed the title [-]Custom decorators does not work with AOT build[/-] [+]Angular compiler - custom decorators does not work with AOT build[/+] on Apr 15, 2017
  2. tbosch commented on Apr 17, 2017

    @tbosch
    Contributor

    Hello,
    could you create a reproduction for this, e.g. a branch on Github?

  3. added
    area: coreIssues related to the framework runtime
    needs reproductionThis issue needs a reproduction in order for the team to investigate further
    on Apr 17, 2017
  4. ArielGueta commented on Apr 17, 2017

    @ArielGueta
    Author

    After investigating, I found the real problem, and it's interesting.

    In my custom decorator I'm trying to override the ngOnDestroy method, for example:

    export function Test() {
      return function ( constructor ) {
       constructor.prototype.ngOnDestroy = function () {
         console.log("ngOnDestroy");
       };
     }
    }

    But -

    1. When working with JIT mode, everything is working fine.

    2. When working with AOT - only when I add the ngOnDestroy explicitly, Angular will call my decorated method, but in JIT mode it's working even if I don't define this method explicitly.

    @Component({
      ...
    })
    @Test()
    export class TodoComponent {
      // When I am in AOT mode, and I don't define this method my decorated method will not run  
      ngOnDestroy() {
      }
    
    }

    Any ideas why this is happening?

  5. changed the title [-]Angular compiler - custom decorators does not work with AOT build[/-] [+]Angular compiler - custom decorators with ngOnDestroy does not work with AOT build[/+] on Apr 18, 2017
  6. robisim74 commented on Apr 30, 2017

    @robisim74
    Contributor

    Same problem here using ngOnInit: I'm creating a class decorator that uses apply to modify the ngOnInit method:

    export function MyClass(): ClassDecorator {
    
        function DecoratorFactory(target: Function): void {
            const targetNgOnInit: Function = target.prototype.ngOnInit;
    
            function ngOnInit(): void {
                console.log("ngOnInit is working");
    
                if (targetNgOnInit) {
                    targetNgOnInit.apply(this);
                }
            }
    
            target.prototype.ngOnInit = ngOnInit;
        }
    
        return DecoratorFactory;
    
    }
    

    It works fine in JiT mode, but in AoT it works only if ngOnInit method already exists in the component.

  7. robisim74 commented on May 1, 2017

    @robisim74
    Contributor

    @tbosch I created a repository: https://github.com/robisim74/angular-decorators

    JiT mode: npm start
    AoT mode: npm run build

    The problem doesn't seem to relate only to lifecycle hooks methods: AoT mode doesn't work also if you try to create a custom property or a custom method into the decorator.

  8. NetanelBasal commented on May 1, 2017

    @NetanelBasal
  9. robisim74 commented on May 9, 2017

    @robisim74
    Contributor

    @tbosch @vicb Any updates about this issue? Is it a bug? Or are there other solutions? Thanks.

  10. tbosch commented on May 10, 2017

    @tbosch
    Contributor

    This is a general "problem" with AOT: It relies on being able to statically analyze things like lifecycle hooks, properties with @Input on them, constructor arguments, ...
    As soon as there is dynamic logic that adds new methods / properties, our AOT compiler does not know about this.

  11. added and removed
    needs reproductionThis issue needs a reproduction in order for the team to investigate further
    on May 10, 2017
  12. tbosch commented on May 10, 2017

    @tbosch
    Contributor

    As we are moving to AOT by default in the future, this won't go away.

    Sorry, but closing as infeasible.

  13. 18 remaining items

  14. trotyl commented on Sep 29, 2018

    @trotyl
    Contributor

    This is already supported in Ivy, no need for any more idea.

    Life-cycle hooks are always checked in runtime in Ivy, but when using decorator it’s not even available at runtime due to evaluation timing. Can only be done in the current (new) decorator proposal, not the deprecated (TypeScript’s) one.

    (By the way, also not work in JIT now if decorator order is wrong for ivy’s checking)

  15. nasreddineskandrani commented on Sep 29, 2018

    @nasreddineskandrani

    @trotyl
    Yep that's what i wrote here #20941
    for now it's delayed, no? o O or i missed something (exclusive to ivy)

  16. trotyl commented on Sep 29, 2018

    @trotyl
    Contributor

    @nasreddineskandrani I don’t know what you mean delayed, that’s a different proposal which being much easier to implement in Ivy, the design and work should only be started after Ivy becoming stable.

  17. nasreddineskandrani commented on Sep 29, 2018

    @nasreddineskandrani

    @trotyl
    the first proposal was not on ivy and they closed it #20682

    The bad news is that we don't want to add it to ViewEngine and than have to reimplement in Ivy because the API will be different in Ivy. So for now I am going to close this PR and we will add this feature to Ivy.

    I didn't try out ivy yet. So when you said it's in ivy i thought they did it already.

  18. trotyl commented on Sep 29, 2018

    @trotyl
    Contributor

    @nasreddineskandrani It's closed because not implementable with ViewEngine's structure, it can never be complete even without being closed. It's much more possible in Ivy but still not that easy.

  19. alshdavid commented on Feb 15, 2019

    @alshdavid

    Why not allow us to use comments to tell the static analysis tool to add certain hooks to classes if not already present?

    Say we decorate the decorator

    /// @angular-aot keep:onInit,onDestroy
    function myDecorator(target, key) {
        ...
    }
    
  20. dinohorvat commented on Mar 27, 2019

    @dinohorvat

    Are there any plans for this? Or it will be ignored completely.

  21. stupidawesome commented on May 26, 2019

    @stupidawesome

    I'm working on a library called NgObservable that implements custom lifecycle decorators that are AoT cross-compatible with ViewEngine and Ivy. The tradeoff is that each component must extend the NgObservable base class to get around the AoT limitations, which in turn implements every angular lifecycle hook leading to slightly larger component factories.

    It's still in the works but I'm keen to get feedback. You can grab it on NPM.

  22. therepo90 commented on Jul 9, 2019

    @therepo90

    Lack of custom decorators is one of the most annoying things in angular...

    There is "jit" flag in module thought that could help but that's not cool.

  23. haskelcurry commented on Aug 17, 2019

    @haskelcurry

    Any custom decorators don't work with AOT, not only method decorators.
    And yes, I agree with @therepo90 that it's very annoying.

  24. jupapios commented on Aug 23, 2019

    @jupapios

    This is not a fix to the actual issue but it's a solution we found to prevent us from leaving decorators that depend on ngOnDestroy without the hook. We make sure the target of the decorator has the OnDestroy type, that way compiling will error if you don't implement OnDestroy.

    example:

    function SomeDecorator(target: OnDestroy) {
        ...
    }
    
    

    if you use it without correctly implement OnDestroy you'll see something like
    error TS2345: Argument of type 'SomeComponent' is not assignable to parameter of type 'OnDestroy'. With that type validation, it won't let you forget about the ngOnDestroy hook by mistake.

    If your decorator adds properties to the target you can use a custom type that extends OnDestroy

    interface SomeDecoratorTarget extends OnDestroy {
      someExtraPropertyThatIsAddedInTheDecorator?: any;
    }
    
    function SomeDecorator(target: SomeDecoratorTarget) {
        ...
    
      if (!target.someExtraPropertyThatIsAddedInTheDecorator) {
        Object.defineProperty(target, 'someExtraPropertyThatIsAddedInTheDecorator', {
          value: 'some value',
        });
     }
    
    }
    
    
  25. Cole-Beckwith-SP commented on Sep 16, 2019

    @Cole-Beckwith-SP
    Contributor

    The existing workaround sounds like it's adding an empty lifecycle hook to the component. Will this continue to work while using Ivy?

  26. robisim74 commented on Sep 17, 2019

    @robisim74
    Contributor
  27. angular-automatic-lock-bot commented on Oct 18, 2019

    @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.

  28. locked and limited conversation to collaborators on Oct 18, 2019
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 runtime

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions