Reputation: 7350
I have a custom component decorator, used to link components to "names", in order to use a JSON object to link components into a hierarchy.
@MyDecorator('name1')
@Component({
selector: 'my-component',
templateUrl: './my-component.component.html',
styleUrls: ['./my-component.component.scss']
})
export class MyComponent implements OnInit {
// ... rest of the implementation
}
What the decorator does is automatically register the component into a Map, pretty much like the following:
{
"name1": MyComponent
}
I have a configuration, pretty much a dynamic route config, stored as a JSON object external to the application code.
[
{
link: '/myroute',
component: 'name1'
}
]
In a dynamic host component, I use code like the following, to instantiate the prescribed component:
// ... gets the component "name"
const componentName = getConfigForRoute('myroute');
// should return MyComponent class, if MyComponent gets included in the app bundle:
const componentType = componentRegistry[componentName];
// componentType is ok with ng serve, and undefined in prod builds!
const cf = this.componentFactoryResolver.resolveComponentFactory(componentType);
// reportOutlet is a ViewContainerRef
this.reportOutlet.createComponent(cf);
Everything works as expected when running in dev mode via ng serve
.
As is often the case, things doesn't go smoothly with a production build: the components decorated with MyDecorator
are not referenced by ts
code, other than the NgModule
of the application, so they are happily dropped by the zealous compiler.
I (like everyone else) used to include them in the EntryComponents
array in the module, but now it seems Ivy is simply ignoring it, so I'm left with no apparent option to make sure the components are not dropped from the build.
@NgModule({
declarations: [
AppComponent,
MyComponent
],
imports: [
BrowserModule,
HttpClientModule,
AppRoutingModule,
// ...some other stuff
],
entryComponents: [MyComponent],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
How can I accomplish the same thing I used to do with EntryComponents or make it work again? That is, make sure MyComponent
gets included? The ideal solution would not include a global list: the whole point of having a decorator is so that I don't need to maintain yet another components list, but I would accept every viable alternative.
Thanks!
Upvotes: 0
Views: 617
Reputation: 1305
The Angular devs suggest exporting the components to the window to ensure they are flagged as being used.
https://github.com/angular/angular-cli/issues/15809
window.retain = [MyComponent];
Upvotes: 1
Reputation: 7350
Well, I hacked my way around the problem, in a very ugly way.
AFAICT, Ivy is letting tsc
and Webpack do their Mojo when dropping classes during tree shake (not sure about the details, this is just the general picture I made).
So in order to "emulate" entryComponents we must include a "usage" of the components somewhere. I don't want to have references to this components outside the module, so I solved the problem with an entryComponents
"emulation".
It is as simple as:
@NgModule({
declarations: [
AppComponent,
MyComponent
],
imports: [
BrowserModule,
HttpClientModule,
AppRoutingModule,
// ...some other stuff
],
entryComponents: [MyComponent],
providers: [
{
provide: '__DEFINITELY_NOT_ENTRY_COMPONENTS__',
// here I can include a list of components not to be dropped by tree shaking
useValue: [MyComponent]
}
],
bootstrap: [AppComponent]
})
export class AppModule {}
I really wish there was a better way.
I think that EntryComponents has its share of use cases, like mine.
Upvotes: 0