Reputation: 578
I'm trying to dynamically create some components based on the configuration options. To be more specific I want to have some toolbar, which can contain different "toolbar items".
As per angular guide: https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html I 've created dedicated directive
@Directive({
selector: '[mte-toolbar-item-host]'
})
export class ToolbarItemDirective {
constructor(public viewContainerRef: ViewContainerRef) {
}
}
which will be used to indicate the "injection" point for the components.
I have the main ToolbarComponent
@Component({
selector: "mte-toolbar",
templateUrl: "./toolbar.component.html",
styleUrls: ["./toolbar.component.css"],
providers: [ToolbarService]
})
export class ToolbarComponent implements AfterViewInit {
toolbar: Toolbar;
@ViewChild(ToolbarItemDirective)
toolbarItemHost: ToolbarItemDirective;
constructor(private toolbarService: ToolbarService, private componentFactoryResolver: ComponentFactoryResolver) {
}
ngAfterViewInit(): void {
this.toolbar = new Toolbar(this.toolbarService.getToolbarItems());
for (let i in this.toolbar.items) {
let toolbarItem = this.toolbar.items[i];
let toolbarItemFactory = this.componentFactoryResolver.resolveComponentFactory(toolbarItem.componentType);
let componentRef = this.toolbarItemHost.viewContainerRef.createComponent(toolbarItemFactory);
componentRef.instance.toolbarItem = toolbarItem;
}
}
}
with appropriate template
<ul class="nav nav-pills">
<li role="presentation" mte-toolbar-item-host></li>
</ul>
For now, the toolbarService
is returning the following list of the components:
export const DEFAULT_TOOLBAR_ITEMS: ToolbarItem[] = [
new GlyphToolbarItem(GlyphToolbarItemComponent, "example-component-id-1", null, "glyphicon-log-in"),
new GlyphToolbarItem(GlyphToolbarItemComponent, "example-component-id-2", null, "glyphicon-floppy-remove"),
new GlyphToolbarItem(GlyphToolbarItemComponent, "example-component-id-3", null, "glyphicon-log-in"),
new GlyphToolbarItem(GlyphToolbarItemComponent, "example-component-id-4", null, "glyphicon-paste")
];
@Injectable()
export class ToolbarService {
getToolbarItems(): ToolbarItem[] {
return DEFAULT_TOOLBAR_ITEMS;
}
}
GlyphToolbarItem
:
export class GlyphToolbarItem extends ToolbarItem {
glyphIcon: string;
constructor(componentType: Type<any>, id: string, command: AbstractCommand, glyphIcon: string) {
super(componentType, id, command);
this.glyphIcon = glyphIcon;
}
}
GlyphToolbarItemComponent
:
@Component({
selector: '[mte-glyph-toolbar-item]',
templateUrl: './glyph-toolbar-item.component.html'
})
export class GlyphToolbarItemComponent {
toolbarItem: ToolbarItem;
}
its template
<a class="btn btn-default btn-sm glyphicon {{toolbarItem.glyphIcon}}"></a>
I was expecting to receive something like:
<ul _ngcontent-c2="" class="nav nav-pills"><!---->
<li role="presentation" mte-glyph-toolbar-item="" ng-version="4.0.3">
<a class="btn btn-default btn-sm glyphicon glyphicon-log-in"></a>
</li>
<li role="presentation" mte-glyph-toolbar-item="" ng-version="4.0.3">
<a class="btn btn-default btn-sm glyphicon glyphicon-floppy-remove"></a>
</li>
<li role="presentation" mte-glyph-toolbar-item="" ng-version="4.0.3">
<a class="btn btn-default btn-sm glyphicon glyphicon-log-in"></a>
</li>
<li role="presentation" mte-glyph-toolbar-item="" ng-version="4.0.3">
<a class="btn btn-default btn-sm glyphicon glyphicon-paste"></a>
</li>
</ul>
but instead I've received the dynamic components wrapped with the <div>
:
<ul _ngcontent-c2="" class="nav nav-pills"><!---->
<div mte-glyph-toolbar-item="" ng-version="4.0.3">
<a class="btn btn-default btn-sm glyphicon glyphicon-log-in"></a>
</div>
<div mte-glyph-toolbar-item="" ng-version="4.0.3">
<a class="btn btn-default btn-sm glyphicon glyphicon-floppy-remove"></a>
</div>
<div mte-glyph-toolbar-item="" ng-version="4.0.3">
<a class="btn btn-default btn-sm glyphicon glyphicon-log-in"></a>
</div>
<div mte-glyph-toolbar-item="" ng-version="4.0.3">
<a class="btn btn-default btn-sm glyphicon glyphicon-paste"></a>
</div>
Why am I receiving the dynamic components wrapped with the <div>
instead of the <li>
element (which seems to be totally ignored)? Is this the proper way to achieve this?
Upvotes: 0
Views: 1692
Reputation: 17899
I think you have to GlyphToolbarItemComponent:
@Component({
selector: 'li[mte-glyph-toolbar-item]',
template: '<a class="btn btn-default btn-sm glyphicon {{toolbarItem.glyphIcon}}"></a>'
})
export class GlyphToolbarItemComponent {
@HostBinding('attr.role') role ="presentation";
toolbarItem: ToolbarItem;
}
and remove empty li + move mte-toolbar-item-host to ul>ng-container
<ul class="nav nav-pills">
<ng-container mte-toolbar-item-host></ng-container>
</ul>
Also I think Angular won't like that you create these components in ngAfterViewInit
I created https://plnkr.co/edit/8cjlzooO1Oh2CWydxbSN?p=info
Upvotes: 1
Reputation: 658087
It doesn't matter what element it is you get the ViewContainerRef
element from. createComponent
will add a sibling and use the selector of the dynamically created component as element. Because no element is given in the selector <div>
is used by default.
If you change the selector from
selector: '[mte-glyph-toolbar-item]',
to
selector: 'li[mte-glyph-toolbar-item]',
or
selector: 'li',
you will get a <li>
element.
Upvotes: 1