Dismiss
Announcing Stack Overflow Documentation

We started with Q&A. Technical documentation is next, and we need your help.

Whether you're a beginner or an experienced developer, you can contribute.

Sign up and start helping → Learn more about Documentation →

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;
    }
}
share|improve this question
1  
Dynamic component loader is obsolete...check this approach stackoverflow.com/a/37044960/1679310 coming with RC – Radim Köhler Jun 3 at 15:34
    
up vote 0 down vote accepted

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): http://plnkr.co/edit/Fbna6uETzKvgpLAOCG3w?p=preview

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.