简体   繁体   中英

In Angular 8, how do I pass a component into another component's input and then use it imperatively in a router module

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

Usually the components that you define in your router are automatically added to the entryComponents, see https://angular.io/guide/entry-components

But sometimes you may have to add them manually:

const COMPONENTS = [ShortLocatesComponent, ShortOrdersComponent];

@NgModule({
  imports: [....],
  declarations: [...COMPONENTS],
  providers: [LocatorService],
  exports: [...COMPONENTS],
  entryComponents: [...COMPONENTS]
})
export class LocatorModule { }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM