I'm trying to update my dynamic component loader from RC4 to RC5 since the ComponentResolver is deprecated. I've updated the loader to the following
@Component({
selector: 'component-dispatcher',
template: `<div #container></div>` // Define the template here because of its brevity
})
export class ComponentDispatcherComponent implements OnInit, OnDestroy {
@Input() component:any; // Some dynamic component to render
@Input() options:any; // Component configuration, optional
@Input() data:any; // Data to render within the component
// Inject the dynamic component onto the DOM
@ViewChild("container", {read: ViewContainerRef}) container:ViewContainerRef;
private componentReference:ComponentRef<any>;
constructor(private resolver:ComponentFactoryResolver) {
}
ngOnInit() {
// Create our component now we're initialised
let componentFactory = this.resolver.resolveComponentFactory(this.component);
this.componentReference = this.container.createComponent(componentFactory);
this.componentReference.instance.data = this.data;
this.componentReference.instance.options = this.options;
}
ngOnDestroy() {
// If we have a component, make sure we destroy it when we lose our owner
if (this.componentReference) {
this.componentReference.destroy();
}
}
}
And attempt to dynamically load the following component into the DOM
@Component({
selector: 'text-cell',
pipes: [IterableObjectPipe],
templateUrl: './text-cell.component.html',
styles: ['.fieldName { font-weight: bold; }']
})
export class TextCellComponent implements OnInit {
// Data to render within the component
@Input() data: any;
@Input() record: any;
// Configuration of what data to display
@Input() options: {
excludeFieldNames: boolean,
translation: string
};
constructor() {
}
ngOnInit() {
setTimeout(() => {
//console.log('***************************** ngOnInit...textCell ***********************');
this.options.translation = '' + (_.get(this.options, 'translation') || 'fields');
});
}
}
Yet when I do this with my TextCellComponent or any other component within the app I get the following error
ORIGINAL EXCEPTION: No component factory found for TextCellComponent
ORIGINAL STACKTRACE:
Error: No component factory found for TextCellComponent
at NoComponentFactoryError.BaseException [as constructor]
(webpack:///./~/@angular/core/src/facade/exceptions.js?:27:23)
at new NoComponentFactoryError
I've completed the steps in
https://angular.io/docs/ts/latest/cookbook/rc4-to-rc5.html
but I seem to be missing something. I've tried adding the components to the bootstrapping and defining them globally with no luck. Any suggestions would be helpful.
EDIT
Adding the module definition
@NgModule({
imports: [
BrowserModule,
HttpModule,
FormsModule,
ReactiveFormsModule,
...MATERIAL_MODULES
],
declarations: [
...APPLICATION_PIPES,
...APPLICATION_COMPONENTS,
...APPLICATION_DIRECTIVES,
CygnusComponent,
// Component declarations
// TODO: refactor to appropriate modules
...
ComponentDispatcherComponent,
TextCellComponent,
...
],
bootstrap: [
ApplicationComponent
],
providers: [
...APPLICATION_PROVIDERS,
AppStore
]
})
export class ApplicationComponent {}
All components about to be loaded "dynamically" need to be declared in the entryComponents
section of your module. In other words you should end up with something like:
@NgModule({
imports: [BrowserModule, HttpModule, FormsModule, ReactiveFormsModule, ...MATERIAL_MODULES],
declarations: [...APPLICATION_PIPES, ...APPLICATION_COMPONENTS, ...APPLICATION_DIRECTIVES, CygnusComponent,
// Component declarations
// TODO: refactor to appropriate modules
...
ComponentDispatcherComponent,
TextCellComponent,
...
entryComponents: [TextCellComponent]
bootstrap: [ApplicationComponent],
providers: [...APPLICATION_PROVIDERS, AppStore]
})
export class ApplicationComponent{
Please note that you need to list the TextCellComponent
in both the declarations
and entryComponents
section.
You may want to check your import paths. In my case, one file import used uppercase while the other used lowercase.
//file 1
import { IRComponent } from "./components/IR/IR.component";
//file 2
import { IRComponent } from "./components/ir/ir.component";
The give away was in the Chrome network tab, I noticed that the file was being loaded twice (once for each spelling).
Let's say TextCellComponent
is declared in FooModule
and your component responsible for creating dynamic content is in module BarModule
.
In such case FooModule
needs to be imported into BarModule
@NgModule({
imports: [FooModule],
declarations: [ComponentDispatcherComponent]
})
export class BarModule {}
To my eyes that kinda compromises the idea of things being dynamic. I simply want a component that will create any component I send it to by class reference. If anyone has a decent solution I'd be glad to hear it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With