In my quest to dynamically load a component in the upcoming release version 2.0.0, I encountered some challenges.
Previously, in RC5, I utilized the following code for loading:
I created a directive responsible for loading the controls:
import {
CheckboxComponent, CheckboxListComponent, DatePickerComponent
} from '../components/';
@Directive({
selector: '[ctrl-factory]'
})
export class ControlFactoryDirective implements OnChanges {
@Input() model: any;
constructor(private vcRef: ViewContainerRef, private resolver: ComponentResolver) {
}
create(cp) {
this.resolver.resolveComponent(cp)
.then(factory => {
const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
this.vcRef.createComponent(factory, 0, injector, []);
let ch = this.vcRef.createComponent(factory, 0, injector, []).instance;
ch.model = this.model;
});
}
ngOnChanges() {
if (!this.model) return;
switch (this.model.type) {
case 'checkbox':
this.create(CheckboxComponent);
break;
case 'checkboxlist':
this.create(CheckboxListComponent);
break;
case 'datepicker':
this.create(DatePickerComponent);
break;
default:
break;
}
}
}
Then, I included that directive on my page like so:
<div ctrl-factory *ngFor="let child of page.childrens" [model]="child"></div>
However, upon transitioning from rc5 to the final release version 2.0.0, I realized that the resolver was replaced by compiler.
I attempted to follow various suggestions on how to achieve this with different codes, but they all seemed too complex and I couldn't get it to work.
For example, check out this link: How can I use/create dynamic template to compile dynamic Component with Angular 2.0?
Although it may be more tailored to a specific scenario, I simply needed to load the component and set an @Input named model.
During my attempts, I had to dynamically create a module for each component and add the component to it. However, I encountered issues indicating that the component was being added to more than one Module, despite trying to remove it unsuccessfully.
The majority of the code I used was sourced from this link:
I made a few modifications based on that reference.
Update
I eventually managed to make it functional by adopting the following approach:
The create method underwent a transformation as follows:
private create(cp) {
@NgModule({
imports: [BrowserModule, ControlsModule],
declarations: []
})
class DynamicModule {}
this.compiler.compileModuleAndAllComponentsAsync(DynamicModule)
.then(({componentFactories}) => {
const compFactory = componentFactories.find(x => x.componentType === cp);
const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
const cmpRef = this.vcRef.createComponent(compFactory, 0, injector, []);
cmpRef.instance.model = this.model;
});
}
Many sources suggested creating the Component and assigning it to the DynamicModule. However, this led to conflicts when the same component was declared in a different module. In my case, importing my ControlsModule - which contains all exported controls - proved to be the solution.