Skip to content

Angular factories should be created after the custom annotations are applied on classes #45477

@cosminadrianpopescu

Description

@cosminadrianpopescu

🐞 Bug report

Command (mark with an x)

  • new
  • build
  • serve
  • test
  • e2e
  • generate
  • add
  • update
  • lint
  • extract-i18n
  • run
  • config
  • help
  • version
  • doc

Is this a regression?

No, it is not a regression

Yes, the previous version in which this bug was not present was: ....

Description

The angular factories (for services or components, for example) are created before the custom annotation on the class is run. This has unwanted side effects in some situations. The angular generated code looks like this:

let AppComponent = class AppComponent {
    constructor() {
        this.title = 'demo-app';
    }
};
AppComponent.ɵfac = function AppComponent_Factory(t) { 
    return new (t || AppComponent)(); // Here, due to the fact that the AppComponent class was declared in the same context as the bellow code, the class modifications will be considered.
 };
...
AppComponent = (0,tslib__WEBPACK_IMPORTED_MODULE_4__.__decorate)([
    src_decorators__WEBPACK_IMPORTED_MODULE_0__.MyCustomDecoration
], AppComponent);

Notice how the factory function is created before applying the custom decorator. When targetting es2017 (like the latest angular version in this moment) this works fine.

But, when targetting esnext, for example, the generated code looks like this:

let AppComponent = class AppComponent {
    title = 'demo-app';
    static ɵfac = function AppComponent_Factory(t) { 
        return new (t || AppComponent)();  // Here, because the factory does not share the same context with where the factory is defined, the AppComponent class is the one before the modification.
    };
...
}

The issue appears when I am modifying the class inside the decorator, like this:

export function DecoratedClass(ctor: any): any {
  const result = class extends ctor {
    constructor(...args: Array<any>) {
      super(...args);
      console.log('i am a modified component', this);
    }
  }
  return result;
}

In this situation, the class changes will not be applied.

A clear and concise description of the problem...

🔬 Minimal Reproduction

You can see here:

https://stackblitz.com/edit/angular-ivy-ctvbax?file=tsconfig.json

Run the project, check the console. You will see the text "I am a modified component". Now change in the tsconfig.json the target to esnext and run the project again. You will see that the text does not appear in console anymore.

🔥 Exception or Error





🌍 Your Environment




Angular CLI: 13.3.0
Node: 14.15.4
Package Manager: npm 6.14.10
OS: linux x64

Angular: 13.3.0
... animations, cli, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1303.0
@angular-devkit/build-angular   13.3.0
@angular-devkit/core            13.3.0
@angular-devkit/schematics      13.3.0
@schematics/angular             13.3.0
rxjs                            7.5.5
typescript                      4.6.3

Anything else relevant?

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: compilerIssues related to `ngc`, Angular's template compiler

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions