AOT Can't resolve all parameters for [...]
Asked Answered
C

2

6

First of all, I must say that my app works fine when "using" JIT. I can even bundle in prod (no AOT, just JIT) and it works fine.

But when I try to compile it (AOT) using ngc I am getting an error, which says:

Can't resolve all parameters for MyComponentView in /path/my-component/my-component.view.ts:
([object Object], [object Object], [object Object], [object Object], ?)

This is the constructor of MyComponent:

constructor( headerService:HeaderService, sidebarService:SidebarService, @Inject( AuthService.Token ) authService:AuthService.Class, router:Router, carbon:Carbon ) {
    …
    this.carbon = carbon;
    …
}

The last dependency (Carbon) is being provided in AppModule like this:

@NgModule( {
    imports: [
        …
    ],
    declarations: [
        …
    ],
    providers: [
        …
        CARBON_PROVIDERS,   //<---- HERE IS BEING PROVIDED
        CARBON_SERVICES_PROVIDERS,
        …
    ],
    bootstrap: [ AppComponent ],
} )
export class AppModule { }

The CARBON_PROVIDERS are being imported using the angular2-carbonldp project which is exporting them like this:

export const CARBON_PROVIDERS:any[] = [
{
        provide: Carbon,
        useFactory(): Context {
            return carbon;
        },
    },
    {
        provide: ContextToken,
        useFactory(): Context {
            return activeContextFn();
        },
    },
    {
        provide: App.Context,
        useFactory(): App.Context {
            if( ! activeContextFn.isAppContext() ) throw new Errors.IllegalStateError( "The activeContext is not an App Context" );
            return <any>activeContextFn();
        },
    },
];

But I end up having the same error and I don't understand WHY! Do you guys happen to know why is it working like that?

Condescendence answered 7/3, 2017 at 16:26 Comment(3)
why does ` useFactory()` have parenthesis?Carboy
That is just using concise object literal method notation which is valid although I would say it should not be preferred here. But it's not the cause of the error.Orchestral
@pixelbits Yes, because of that. [Check this][github.com/angular/angular/issues/13614] so you can see how can it be used. But regarding the missing provider... any ideas why this works JIT but no AOT?Condescendence
O
7

Because the AOT compiler supports only a small subset of JavaScript. This fact is poorly documented, where documented at all, and not widely publicized.

It is unable to parse many JavaScript expressions inside of Angular 2 framework decorators. This includes function calls they are not legal.

The following code causes failure

useFactory(): Context {
  return activeContextFn();
}

The reason for this is that angular2 decorators under AOT are not decorators at all.

The Angular 2 AOT compiler maintains a list of known decorators that it does not persist at runtime, instead treating them as nothing more than annotations*. This means they can only contain constant expressions, references to exported names, a few forms of array literal syntax and object literal syntax that are composed of constant expressions, and little else.

In case you're wondering, yes this means you are no longer writing TypeScript code and thusly are no longer writing JavaScript code. Incidentally it also means you can not trust most of your tools as they will give you a green light for invalid code because they are not aware of the language.

It is a different language with different semantics and not simply a subset because the decorator transform that it performs violates the specification for that feature in ECMAScript. Typescript does not add or modify any runtime behavior of ECMAScript.

*For reference, the term annotation is used here as it relates to the now dead at @Script language. This is the language the Angular team invented to write their framework in and started using heavily, via dedicated transpiler, before they moved to TypeScript.

In this specific case I think you need to rewrite your factory as follows:

useFactory: contextFactory

....

export function contextFactory() {
    return activeContextFn();
}

This may not be all that you need to do and the language will no doubt change over time. It is slated to have IDE support.

Here is a fairly useful "sandbox" https://github.com/rangle/angular-2-aot-sandbox

Be aware that the repository linked above is not an official source of documentation and that it defines its own subset based on its capabilities. Use with caution.

Orchestral answered 7/3, 2017 at 16:50 Comment(9)
I liked A LOT your explanation regarding why the annotations are useless with code inside them. It helped me to understand the need to export functions and use them outside the annotation when AOT compiling. With that in mind, I proceeded to export the functions like this: ` export function aotCarbonFactory():Context { return carbon; } export const CARBON_PROVIDERS:any[] = [ { provide: Carbon, useFactory: aotCarbonFactory, } ]` But the result is still the same. Do you to know why?Condescendence
I believe carbon itself needs to be exported but I'm not positive.Orchestral
I added a link to an unofficial AOT sandbox to the the answer. You may find it helpful but read the caveats.Orchestral
Thanks for the sandbox project, gonna take a look to that! I have one final question... Do you think that the Carbon library needs to be AOT compliant too? I mean, does the library must provide the metdata.json files also? (Implement AOT)Condescendence
If it uses Angular 2 then yes the AOT requirment is 100% transitive.Orchestral
The Carbon project [github.com/CarbonLDP/carbonldp-js-sdk] doesn't use Angular at all. It's just a library with nothing to do with Angular. Because of that it doesn't have to export/provide the .metadata.json files, right? I mean, it can be used as is, right?Condescendence
Link is a 404. How are you importing it?Orchestral
Here's the link: github.com/CarbonLDP/carbonldp-js-sdk I am also importing it like this: import Carbon from "carbonldp/Carbon";Condescendence
Yeah non-Angular2 dependencies can't and don't provide AOT metadata. Your import is correct (BTW its nice to see a package with an actual default export). I'm not sure why it isn't working but try replacing it with return null and see if that works.Orchestral
W
1

For anyone else searching this specific error text:

I encountered this error when building a parent angular project with AOT enabled that had a child angular library project I had packaged with ng-packagr, which used barrels internally. I was able to solve the problem by changing all of the barrel references in the library project to explicit references.

Before

import { MyService } from './featureFolder';

After

import { MyService } from './featureFolder/my.service';

Issue Link The dev team claims this was fixed, but it seems to be back in some form in Angular 8. https://github.com/angular/angular/issues/23713

Walsingham answered 14/11, 2019 at 21:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.