I have already looked at the following two threads that were about using a component passed in declaratively (1, 2, contentChildren, ng-content) in the template, I am asking how one could pass in a component and use it imperatively (in a routing module).
The use case will be for use in a component library. Passing in a component into the package to be automatically routed to and displayed using the router outlet. In other words, I am working on the interface of an 'angular npm package' and an 'angular application' trying to pass an angular component among other routing setup properties from the angular application to the angular npm package which will be used to pass information to set up a route that routes to that component.
So at the moment, it would start with the application-specific data being passed into the package's public API using this app-collections object:
@Component({
selector: 'app-geode-app-test',
templateUrl: './geode-app-test.component.html',
styleUrls: ['./geode-app-test.component.scss']
})
export class GeodeAppTestComponent implements OnInit {
environment: Environment;
appCollections: AppCollectionInterface[];
constructor() { }
ngOnInit() {
this.environment = environment;
this.appCollections = [
{
'name': 'Short Locates',
'routerId': 'shortLocates',
'image': null,
'entitlements': 'SHORT_LOCATE_READ',
'component': ShortLocatesComponent
},
{
'name': 'Short Sell Orders',
'routerId': 'shortSellOrders',
'image': null,
'entitlements': 'SHORT_SELL_ORDER_READ,SHORT_SELL_ORDER_READ_MY_FUNDS',
'component': ShortOrdersComponent
},
]
}
}
Then, in its template, it would interface with the package like this:
<lib-geode-app [environment]="environment" [appCollections]="appCollections"></lib-geode-app>
Ignore the environment input. We are focusing on the appCollections input.
In the package's topmost component where its inputs are coming in, I am updating a package wide store OnChanges to the input:
ngOnChanges(changes: SimpleChanges): void {
console.log('%c⧭', 'color: #00e600', changes);
if (changes.environment.currentValue) {
this.environmentSvc.environmentSubject.next(changes.environment.currentValue)
}
if (changes.appCollections.currentValue) {
console.log('%c⧭', 'color: #f2ceb6', changes.appCollections.currentValue);
this.appCollSvc.appCollectionsSubject.next(changes.appCollections.currentValue)
}
}
This store is working fine all the information is successfully syncing in any components I need it to be:
Now when I bring it into the routing module, this is where things get confusing. I would love to just be able to put the component data coming indirectly into the Routes array but it doesn't seem like I can because that is being used within the NgModule metadata before initialization. So instead, I am updating the Routes config and unshifting these routes onto it in the appConfigFunction which is being called (after the module metadata is done processing?) in the constructor.
The router links are working and pointed properly to these address locations, but when I click them they throw this error:
core.js:6014 ERROR Error: Uncaught (in promise): Error: No component factory found for ShortOrdersComponent. Did you add it to @NgModule.entryComponents? Error: No component factory found for ShortOrdersComponent. Did you add it to @NgModule.entryComponents? at noComponentFactoryError (core.js:25607) at CodegenComponentFactoryResolver.resolveComponentFactory (core.js:25683) at RouterOutlet.activateWith (router.js:8757) at ActivateRoutes.activateRoutes (router.js:4010) at router.js:3947 at Array.forEach () at ActivateRoutes.activateChildRoutes (router.js:3942) at ActivateRoutes.activate (router.js:3805) at MapSubscriber.project (router.js:3778) at MapSubscriber._next (map.js:29) at resolvePromise (zone-evergreen.js:797) at resolvePromise (zone-evergreen.js:754) at zone-evergreen.js:858 at ZoneDelegate.invokeTask (zone-evergreen.js:391) at Object.onInvokeTask (core.js:39680) at ZoneDelegate.invokeTask (zone-evergreen.js:390) at Zone.runTask (zone-evergreen.js:168) at drainMicroTaskQueue (zone-evergreen.js:559) at ZoneTask.invokeTask [as invoke] (zone-evergreen.js:469) at invokeTask (zone-evergreen.js:1603) defaultErrorLogger @ core.js:6014 handleError @ core.js:6066 next @ core.js:40558 schedulerFn @ core.js:35336 __tryOrUnsub @ Subscriber.js:183 next @ Subscriber.js:122 _next @ Subscriber.js:72 next @ Subscriber.js:49 next @ Subject.js:39 emit @ core.js:35298 (anonymous) @ core.js:39738 invoke @ zone-evergreen.js:359 run @ zone-evergreen.js:124 runOutsideAngular @ core.js:39572 onHandleError @ core.js:39735 handleError @ zone-evergreen.js:363 runGuarded @ zone-evergreen.js:137 api.microtaskDrainDone @ zone-evergreen.js:663 drainMicroTaskQueue @ zone-evergreen.js:566 invokeTask @ zone-evergreen.js:469 invokeTask @ zone-evergreen.js:1603 globalZoneAwareCallback @ zone-evergreen.js:1629
I read about entryComponents and am unsure how I could add these components to the packages entryComponents, but also wondering why I even need to do that as I am receiving the entire component instance through the input successfully as you can see in the above console log. I am also importing (and exporting just in case that made a difference) that component that I am feeding into the package, already in the app itself (in that components module):
@NgModule({
imports: [....],
declarations: [
ShortLocatesComponent,
ShortOrdersComponent,
],
providers: [LocatorService],
exports: [
ShortLocatesComponent,
ShortOrdersComponent,
]
})
export class LocatorModule { }
How do I make this error go away and add it to the entryComponents or make it so I don't have to add it to the entryComponents?
Please let me know if I could be more clear on anything!
Thanks