Context - I'm trying to create a custom dropdown that can contain a number of components. I could accomplish this via the <ng-content>
tag, but my team is stubbornly insisting that they don't like that. They want to be able to instantiate this dropdown almost entirely through typescript code.
I think I could accomplish this through the DynamicComponentLoader, but unfortunately, all the good tutorials I've found use the loadIntoLocation() function, which is now gone. So instead I've tried to use the loadAsRoot() function, but it's not working.
Here's what I'm trying to do:
Main.ts:
import { Component } from '@angular/core';
import { MyDropdown } from './MyDropdown';
@Component({
selector: 'my-app',
template: `
<my-dropdown [contentModels]="dropdownContentModels"></my-dropdown>
`
})
export class Main {
dropdownContentModels: any[];
constructor() {
var someComponentModel = {selector: 'some-component', text: 'some'};
var otherComponentModel = {selector: 'other-component', text: 'other'};
this.dropdownContentModels = [someComponentModel, otherComponentModel];
}
}
MyDropdown.ts:
import { Component } from '@angular/core';
import { InjectComponent } from './InjectComponent';
@Component({
selector: 'my-dropdown',
inputs: ['contentModels'],
directives: [InjectComponent],
template: `
<div class="btn-group" dropdown>
<button type="button" dropdownToggle>My Dropdown</button>
<div class="dropdown-menu" role="menu">
<inject-component *ngFor="let item of contentModels" [model]="item"></inject-component>
</div>
</div>
`
})
export class MyDropdown {
contentModels: any[];
}
InjectComponent.ts:
import { Component, DynamicComponentLoader, Injector } from '@angular/core';
@Component({
selector: 'inject-component',
inputs: ['model'],
template: `
<div #toreplace></div>
`,
providers: [DynamicComponentLoader, Injector]
})
export class InjectComponent {
model: any;
constructor(private dcl: DynamicComponentLoader, private injector: Injector) {}
ngOnInit() {
this.dcl.loadAsRoot(this.createWrapper(), '#toreplace', this.injector);
}
createWrapper(): any {
var model = this.model;
@Component({
selector: model.selector + '-wrapper',
template: '<' + model.selector + ' [model]="model"></' + model.selector + '>'
})
class Wrapper {
model: any = model;
}
return Wrapper;
}
}
But I'm getting the runtime exception "EXCEPTION: Error: Uncaught (in promise): Can only add to a TokenMap! Token: Injector"
Update! (Thanks to echonax):
InjectComponent.ts:
import { Component, ComponentResolver, ViewChild, ViewContainerRef,
ComponentFactory, ComponentRef } from '@angular/core';
@Component({
selector: 'inject-component',
inputs: ['model'],
template: `
<div #toreplace></div>
`
})
export class InjectComponent {
model: any;
@ViewChild('toreplace', {read: ViewContainerRef}) toreplace;
componentRef: ComponentRef<any>;
constructor(private resolver: ComponentResolver) {}
ngOnInit() {
this.resolver.resolveComponent(this.createWrapper()).then((factory:ComponentFactory<any>) => {
this.componentRef = this.toreplace.createComponent(factory);
});
}
createWrapper(): any {
var model = this.model;
@Component({
selector: model.selector + '-wrapper',
directives: [ model.directives ],
template: '<' + model.selector + ' [model]="model"></' + model.selector + '>'
})
class Wrapper {
model: any = model;
}
return Wrapper;
}
}
You can use the new .createComponent()
function.
import {ComponentRef, Injectable, Component, Injector, ViewContainerRef, ViewChild,ComponentResolver, DynamicComponentLoader} from '@angular/core';
export class InjectComponent {
@ViewChild('toreplace', {read: ViewContainerRef}) toreplace;
constructor(private dcl: DynamicComponentLoader, injector: Injector,private resolver: ComponentResolver) {}
...
this.resolver.resolveComponent((this.createWrapper()).then((factory:ComponentFactory<any>) => {
this.cmpRef = this.theBody.createComponent(factory)
});
and remove providers: [DynamicComponentLoader, Injector]
Here's an example plunker that uses DynamicComponentLoader (in app.component.ts): https://plnkr.co/edit/azoGdAUvDvCwJ3RsPXD6?p=preview
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