angular: Function calls are not supported in decorators when fullTemplateTypeCheck is not specified and @dynamic has no effect
I’m submitting a…
[X] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report
[ ] Performance issue
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
[ ] Other... Please describe:
Current behavior
When building a library and specifying both skipTemplateCodegen
and strictMetadataEmit
to true
, and you try to call a method inside the ngModule
decorate example RouterModule.forChild([])
this will cause a compilation error Function calls are not supported in decorators but 'RouterModule' was called.
When adding fullTemplateTypeCheck
to true
the error is not emitted.
Also, @dynamic
seems not to have any effect in this particular case.
Expected behavior
No error emitted, or at least that it can be suppressed with the @dynamic
.
I also expect that setting fullTemplateTypeCheck
doesn’t have any effect here as this flag relates more to binding in templates.
Minimal reproduction of the problem with instructions
Create a file example
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
@NgModule({
imports: [
RouterModule.forChild([])
]
})
export class LibModule { }
Have tsconfig set with the following options;
"angularCompilerOptions": {
"skipTemplateCodegen": true,
"strictMetadataEmit": true
}
When transpiling this will emit an error;
Error during template compile of 'LibModule' Function calls are not supported in decorators but 'RouterModule' was called.
Note when adding "fullTemplateTypeCheck": true
no error is emitted. (Though this is kinda weird as I don’t think this shouldn’t have any effect)
Reproduction repo: https://github.com/alan-agius4/angular-issue-23609
What is the motivation / use case for changing the behavior?
No error was emitted in NG 5
Environment
Angular version: 6.0.0-rc-6
Related issues: https://github.com/dherges/ng-packagr/issues/822 https://github.com/dherges/ng-packagr/issues/778 https://github.com/dherges/ng-packagr/issues/727 https://github.com/dherges/ng-packagr/issues/765 https://github.com/dherges/ng-packagr/issues/767 https://github.com/dherges/ng-packagr/issues/885
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 108
- Comments: 117 (32 by maintainers)
Commits related to this issue
- feat: enable `fullTemplateTypeCheck` (#826) Angular recommends to turn this flag on for packages and libraries https://github.com/angular/angular/blob/master/packages/compiler/design/separate_comp... — committed to ng-packagr/ng-packagr by alan-agius4 6 years ago
- refactor: NgrxDataModule no longer calls store/effects .forFeature * Inspired by inability to build because of issue https://github.com/angular/angular/issues/23609 * other refactors to satisfy RxJS... — committed to johnpapa/angular-ngrx-data by wardbell 6 years ago
- breaking changes: update to Angular/RxJS/Ngrx v6 v6.0.0-beta.1 (#147) * tweak collection reducer methods * refactor: NgrxDataModule no longer calls store/effects .forFeature, Inspired by inabilit... — committed to johnpapa/angular-ngrx-data by wardbell 6 years ago
- chore(ng): disable prod build until ngx-progrss bug is fixed see: https://github.com/angular/angular/issues/23609 — committed to travelfeed/travelfeed-app by deleted user 6 years ago
- chore(npm): upgrade ngx-progressbar as the module bug was fixed 🎉 - see: https://github.com/angular/angular/issues/23609 - https://github.com/MurhafSousli/ngx-progressbar/pull/168 — committed to travelfeed/travelfeed-app by deleted user 6 years ago
- https://github.com/angular/angular/issues/23609 — committed to startwire/startwire-angular-lib by mflorence99 6 years ago
- refactor(ngx-api-utils): refactor the `NgxApiModule` and remove `forRoot` as it breaks The error shown is: ``` ERROR in Error during template compile of AppModule Function calls are not supported i... — committed to ngx-api-utils/ngx-api-utils by smoke 6 years ago
- fix packaging error in angular-requests... `forRoot` cannot contain any variables or functions. Crazy. see https://github.com/angular/angular/issues/23609#issuecomment-412361436 — committed to flixpressllc/flixpress-angular-lib by happycollision 6 years ago
- fix(build): ngc see: https://github.com/angular/angular/issues/23609 — committed to ammora-team/ionic-cache by ramonornela 5 years ago
- fix(build): ngc see: https://github.com/angular/angular/issues/23609 — committed to ammora-team/ionic-cache by ramonornela 5 years ago
- add fullTemplateTypeCheck option for avoid this error Error during template compile of 'NgrxJsonApiModule' Function calls are not supported in decorators but 'EffectsModule' was called. source: h... — committed to kawamoto/ngrx-json-api by kawamoto 5 years ago
- add fullTemplateTypeCheck option for avoid this error Error during template compile of 'NgrxJsonApiModule' Function calls are not supported in decorators but 'EffectsModule' was called. source: h... — committed to kawamoto/ngrx-json-api by kawamoto 5 years ago
- add fullTemplateTypeCheck option for avoid this error Error during template compile of 'NgrxJsonApiModule' Function calls are not supported in decorators but 'EffectsModule' was called. source: h... — committed to abdulhaq-e/ngrx-json-api by kawamoto 5 years ago
- breaking changes: update to Angular/RxJS/Ngrx v6 v6.0.0-beta.1 (#147) * tweak collection reducer methods * refactor: NgrxDataModule no longer calls store/effects .forFeature, Inspired by inabilit... — committed to devstar0826/angular-ngrx-data by wardbell 6 years ago
Regarding Ward’s repro: @wardbell
The build will succeed / fail depending on the combination of
angularCompilerOptions
. Added one example in this reproBuild success
Build failures
Observation
In Ward’s example, setting
"skipTemplateCodegen": true
requires that"fullTemplateTypeCheck": true
is also enabled to get a success build from ngc.Side notes / other thoughts
My experience is that the issue goes down to “everything statically analyzable for AoT” (see “need for static value resoltion” in the compiler docs). Imo, something is broken around the
strictMetadataEmit
option or my understanding of the option is horribly broken 😆From my experience I can tell that the issue is often produced in
static forRoot(): ModuleWithProviders
, sometimes depending on one line of code that becomes or becomes not “statically analyzable”. Of course, I don’t have reliable repros and I also don’t want to speculate on some vague memories of code that I’ve seen working / non-working.Can ngc improve the error message?
What will help that ngc prints out the line number in the source code that triggers the error.
To the next poor soul who comes across this ridiculously long chain of comments, and historically difficult compile error to resolve. I hope you find this comment, and it helps you.
My library uses a
forRoot()
static function to provide a configuration. This library compiles fine and had no issues working with an Angular application compiled with 8.2 or lower, but this error appears as soon as I started using 8.3 or higher to compile an application that uses the library.The error specifically was this:
The
MainModule
is in my application, andLoggerModule
was in the library. The library was compiled with 8.0 and works fine with apps upto 8.2, but breaks with 8.3 or higher.Here are the steps that you can try if this happens to you:
Make sure that your
tsconfig.lib.json
has the followingNow for the
NgModule
that has the static function it’s really important to do the following things:return
statement that yields aModuleWithProviders
object.providers
providers
should have anexport
but don’t have to be exposed in yourpublic_api
// @dynamic
above my@NgModule()
providers
ModuleWithProviders<LoggerModule>
The above restrictions complicated things fo mer, because I had to figure out how to fix this issue while remaining compatible with everyone who was using the library.
This is what the
LoggerModule
ended up being, and this works for me:The key things to notice is that the
forRoot()
is just a function that returns a module metadata, and there is no other source code. The other thing is that this limitation forced me to use a factory, and then figure out how to pass theloggerConfig
options to the factory method.As a comparison, I’ll also share below what the original source code was that broke so you can see how dramatically different the fix is from the original. The fact that I got this working is in itself a miracle.
This is the second highest commented issue in the past year, second only to the Ivy tracking issue. It’s a fundamental problem with AOT and its status is backlog. Can we “needsTriage” this?
It helps me
Can anyone tell a nub what should I do for now to workaround this issue?
ng build --prod
I’m using ngx-progressbar and i get : “Function calls are not supported in decorators but ‘NgProgressModule’ was called.”I found another workaround that isn’t as ugly as my prior workaround. You can see where I found this, here: https://github.com/dschnelldavis/angular2-json-schema-form/issues/273#issuecomment-407184242. So, here is my workaround for the example I gave above with ngrx.
Please note that you must declare
ModuleWithProviders
, otherwise, another error occurs.I fixed that by replacing import of
RouterModule.forChild(routes)
with the previously defined constant:export const routerModule = RouterModule.forChild(routes);
I started getting this issue in one of my libraries once I repackaged it with Angular 7.0 (up from 6.x). Tried every applicable suggestion in this thread with no luck. My
forRoot
is as static as they get.Library
./misc/injection-tokens.ts
NgForage.module.ts
Test app
Related bug reports in the library repo:
For the record, I have this problem with latest angular and ng-packagr (3.0.0) and the three aforementioned parameters are set to true (this is the default when generating a library with angular-cl). So I’m not sure the PR you made @JoostK actually fixes this problem… or something new appeared with latest versions. This is really a pain in the ass because there is no way to understand why something works or not (at one point I made it work, but I am unable to know why or how I can go back to this working state), I second @dherges request to have better error output…
EDIT: I found the source of my problem, it was a wrong
paths
declaration in tsconfig preventing metadata to be found. Neverttheless, we are really missing something to diagnose those problems…I’ve managed to fixed this by re-exporting the actual file in public_api.ts
Example of the issue:
File ~/lib/index.ts
export * from './sample/data.component';
File ~/public_api.ts
export * from './lib';
This would work on
ng build --prod --aot=false
, but it won’t build tong build --prod
…to fix: File ~/public_api.ts
export * from '.lib/sample/data.component;'
I think the answer will always be the same… Will be much easier to fix once ivy is out™
Let’s just brace ourselves and wait for ivy to come 🤷🏻♂️
This has arguably been the most annoying error that libraries have had to deal with, and I’m glad that this class of issues has been resolved with the Ivy compiler.
With v12 now allowing libraries to be compiled with the Ivy compiler, I am closing this issue as resolved.
Any update ? It’s blocking our migration from angular 5 to 6. Our libraries contains 3 forRoot. I don’t like the solution to copy the forRoot code of the external libraries to our project.
Hi @jpike88, all.
I agree, it’s pretty crazy that no line/column context for the error is provided. Unfortunately it’s not an easy fix - the problem is more systemic than adding some missing info to the error message.
ngc (specifically, the View Engine compiler) begins by extracting “metadata” from the source code. Each
.ts
file becomes a JSON structure describing it. The rest of the compiler operates on this metadata. Contextual information (line/col numbers) are captured for some structures, but not all, as that would greatly increase the metadata size and hurt compiler performance.The validation done by
strictMetadataEmit
happens on this metadata, and so whether or not the line/col context of an error can be displayed depends on the type of error and whether the metadata has captured this information in the first place. The two systems are relatively separate, and so it’s not always guaranteed that contextual information will be available.That’s also why you see little quality-of-life bugs like this not getting addressed over the last couple years. Rather than try and deal with all of the issues like this individually, we’ve been focused on replacing the metadata design as a whole with a system that doesn’t have these kinds of issues. The Ivy compiler is incredibly good at localizing errors and telling you exactly why something isn’t valid, because that has been one of its design goals since day 1.
So that’s really our fix for this issue - Ivy will eventually make confusing and inconsistent metadata errors like this a thing of the past. With libraries the situation is a little complicated since for backwards compatibility, they still have to publish in View Engine format and thus deal with
strictMetadataEmit
for a little longer, but that’s going to change sooner rather than later, hopefully.@rahulsahay19 honestly? You are screwed, this is hell and nobody care 😃 I’ve stopped trying to solve the problem.
The only thing that I found to work is to not use
forRoot
at all and to duplicate in each module that use your module the content of theproviders
map offorRoot
. But your mileage may vary 😃Thanks to help from @samherrmann, I have been able to get my code working.
It appears that the AOT-enabled compilation process has problems dealing with
import
statements that rely onindex.ts
files inside directories.Firstly, an explicit export statement for the Angular Modules was necessary within the library
public_api.ts
file to get rid of the errorFunction calls are not supported in decorators but 'Module' was called.
.But even after that, all directory-based import statements within the library code, which rely on
index.ts
files within those directories to export the other modules, had to be changed to import directly from the module files themselves.i.e. change:
to
Look at https://github.com/kiranjholla/ng-issue-23609-repro/issues/1#issuecomment-417449088 for details.
@danielmhair Thanks! I actually had tried your fix too and that too had not worked for me.
However, I have now discovered that the problem wasn’t in this piece of code or the fixes that had been suggested here, but rather in a different piece of my code. It was in the way I was deriving the
config
object that was being passed into thisforRoot
method when importing theAppSettingsModule
.The Problem
The Fix
To fix this, I replaced this with
The trick seems to be to ensure that there is absolutely no code-execution necessary before the
forRoot
is invoked. All executable code should be only within the factory. TheforRoot
method can only deal with completely static values.I think I had the same issue. In my case the metadata of the used library was not generated correctly:
Then I changed the public_api.ts in the library project:
Now it works fine. Maybe this helps to resolve the issue?
Toggle Ivy in a library
I wanted to comment again, because I have came into this error for the 5th time while updating all my demo apps, main app and libraries (3 demos, 1 main app, and 3 libraries). I receive these errors in various modules. One was referenced above (formly-js/ngx-formly#996) and then another that I just receive from @ngrx/core, where it says the same error:
The other 3 are from
forRoot
methods in my own modules. The common theme with all these, is that they all useInjectionToken
s. I’m quite convinced that this is only related to modules that useforRoot
methods providing a value for anInjectionToken
.This is forcing me to copy all that is in those forRoot methods and paste them into the module I need them in. This is becoming very taxing, especially when those InjectionTokens are from libraries I don’t maintain.
Anyway, I really appreciate all the effort that has gone into resolving this error. If there is anything I can do to help with this, I’m open to help. I’m not really sure what to do, to help with this one. Seems like its pinned down on what is happening. Hopefully it can get resolved soon.
I would suggest my fix. Its temporary, but not overbearing in how bad it looks. Not sure if it will help, but it helped me in about 5 scenarios quite like this.
In
MyAppModule
, when you callforRoot
, have it be exported into a const variable such as:Make sure you import
ModuleWithProviders
from@angular/core
and that you put it as a typing. It might not work if you do not type theappSettingsModuleForRoot
variable.After reading @leonardochaia 's answer I just fixed this error on my lib import… by removing a
console.log()
statement from theforRoot
method declarationAfter some struggle it seems that
forRoot
must be a function with a return statement and nothing more.This menas that, variables and functions can not be used inside the method.
WORKS
DOESNT WORK Storing the object in a variable, or using any function breaks the build.
Commit fixing this in Timoneer
A good example is the RouterModule
@shairez I wonder if it is that issue. My problem is, due to my mono-repo, I need to link my libraries to my application via
npm link
. Hopefully there is another solution.I had a similar error which only happened using
yarn link
. I fixed it by installing from the source.+1 For now, it’s fine to leave provide configuration inside the module which is using my lib, but in the future would be great if someone could come and say the status of this fix.
That is something we should fix in the docs, then, as it’s not the case anymore since v12.
Other than that I think I got beaten by @NiklasPor, who has just given the correct answer 😃
Hey, there; Didn’t downvote 😃 ; you probably mistyped me instead
@SergeyMell
@kroeder You, sir, made my day! Thanks!
Does anyone know whether this is documented? And why this happens?
I fixed the error by following https://github.com/angular/angular/issues/23609#issuecomment-561615821
I had to remove this
every angular version, i have to patch angular compiler to suppress this error.
the patch file almost like this
sh.exec(
patch -p0 -i tools/cli-patches/version/enable-angular-compiler-annotation.${version}.patch);
So I went to apply my changes to a second library I have and I discovered something disturbing and “explains” why my minor change to my setup above started behaving differently. My minor change above was mostly just that I added a different tsconfig.json file for building vs. serving. So for building I added the
--ts--config <tsconfig.json>
to the build. I ran into a different error now on this other library and discovered in trying to debug it that …and
give different results! What? I’m passing it the default json file and the first works fine and the second gives me my error. What the heck is going on with that?
I had an issue with this and solved it. It killed a whole day (thanks for that…) but it looks like I got around it.
(For the record, no amount of jiggling around angularCompilerOptions, or exported functions, etc, had any effect).
My issue was, that I wanted to import a module, in a module that was imported by a lazy loaded module, based on the environment data fed in from the top level application. The lazy loaded module can be used in any application in the repo, so the environment data needs to always come from whatever app.module is doing the lazy loading (so the environment data can change).
I figured forRoot was the way to go about it, so did my thing (simplified code below). Note that the class containing the forRoot function does NOT have an ngModule deco (since I’m returning the one from the forRoot function, which is complete, believe it or not the approach came from my “working without TS” angular days, where you didn’t have all the fancy decos):
Worked like a charm…until --prod. Then the horrible error we all see here.
After reading everything there is to read on the subject (including the entire Angular Compiler doc), I came up with this, and it worked. Not pretty but was critical, a big part of my “super tooled mono repo” strategy depends on it.
In the module file:
If I did this:
Or any manner of checking that incoming environment object, like this:
It broke again.
I have some suspicions as to why this works, in a way I think it makes sense based on what I read in the compiler docs, and although it’s not typically done I suspect maybe typing the environment object might have some effect, but I’d have to admit I’d be taking educated guesses at best, so I’ll forego the rationale. At this point I’m just happy I figured it out before having to go into overtime.
Hope that helps somebody out there.
@rahulsahay19 the error is definitely not ideal and this is an issue we should fix; sorry for your frustration. If downgrading to version 6 would be the easiest workaround for you, you can go ahead. We didn’t do major changes between v6 and v7.
OK, I spoke too soon.; this is like peeling an onion! This issue is still not completely fixed.
Context
The
AppSettingsModule
that I keep referring to in my previous posts in the thread above are actually part of a different Library application within the same Angular CLI workspace.As I was working on this project, to investigate another issue, I had temporarily updated my
tsconfig.json
to point to the Library application directly instead of via thedist
folder.Original
tsconfig.json
settingsModified
tsconfig.json
settingsWith the above modified settings, the build works just fine.
However, if I revert the
tsconfig.json
to the original settings, the build fails.I have now created a github repo to illustrate this problem. If you clone that repo and run
npm run repro
, you will see the error I am referring to.How should shared modules that use data passed in from the Application context for initialization be handled? I am trying the solution suggested in https://github.com/angular/angular-cli/issues/9358#issuecomment-373053053, but unsuccessfully.
My scenario is as below:
My Code:
Error:
Any help?
@leonardochaia that is correct, because it must be a completely static function, including what it returns, which is why it cannot have a const in there.
@dherges what about installing the dependencies of my library in my app?
It will work well to copy the library from
dist/my-lib
tomy-app/node_modules/@my/lib
, but what about installing the dependencies ofmy-lib
inmy-app/node_modules
?