Ligar modelo e modelo dinamicamente ao nó DOM no Angular 2

Versão curta

estePlunker define um<view> componente que pode renderizar um modelo arbitrário + modelo. Isso precisa ser alterado parasubstituir o conteúdo renderizado anteriormente em vez de anexar novos pares.

EDIT: Isso está funcionando agora, graças à resposta do usuário3636086.

Um problema ainda permanece: diferente do Angular 1, o Angular 2 me obriga a criar um componente aninhado para atualizar um modelo (já que os modelos são efetivamente uma propriedade estática dos componentes de um componente).classe), então eu tenho um monte de nós DOM desnecessários sendo adicionados.

Versão longaAngular 1

Em nosso projeto, preferimosa maioria do nosso código para não ter dependência direta de uma estrutura de interface do usuário. Temos uma classe viewmodel que une um modelo e uma visualização. Aqui estão exemplos simplificados:

interface IView {
    template: string;
}

class SalesView implements IView  {
    sales: number = 100;
    get template() { return "<p>Current sales: {{model.sales}} widgets.<p>"; }
}

class CalendarView implements IView {
    eventName: string = "Christmas Party";
    get template() { return "<p>Next event: {{model.eventName}}.<p>"; }
}

class CompositeView implements IView  {
    calendarView = new CalendarView();
    salesView = new SalesView();
    get template() { return 
        `<div view='model.salesView'></div>
        <div view='model.calendarView'></div>`; 
    }
}

Nós temos umaview diretiva que pode exibir uma destas visualizações:

<div view='viewInstance'></div>

E seviewInstance alterações, um novo objeto View é renderizado (modelo + modelo) nesse local no DOM. Por exemplo, essa visualização do painel pode ter uma lista arbitrária de visualizações que pode ser renderizada:

class Dashboard implements IView {
    views: Array<IView> = [ new SalesView(), new CalendarView(), new CompositiveView() ];
    activeView: View;
    get template() { return "<h1>Dashboard</h1>  <div view='model.activeView'>"; }
}

Um ponto crucial é que isso é compostável. o<view> pode conter um<view> que pode conter um<view>, e assim por diante.

No Angular 1, nossoview diretiva é algo como isto:

.directive("View", [ "$compile",
    ($compile: ng.ICompileService) => {
        return <ng.IDirective> {
            restrict: "A",
            scope: { model: "=View" },
            link(scope: ng.IScope, e: ng.IAugmentedJQuery, atts: ng.IAttributes): void {
                scope.$watch((scope: any) => scope.model, (newValue: any) => {
                    e.html(newValue.template);
                    $compile(e.contents())(scope.$new());
                });
            }
        };
    }
]);
Angular 2

Estou tentando portar isso para o Angular 2, mas carregar dinamicamente um novo modelo em um local DOM é muito desajeitado, forçando-me a criar sempre um novo tipo de componente.

Este é o melhor que eu criei (atualizado com o feedback do usuário3636086):

@Component({
  selector: 'view',
  template: '<span #attach></span>',
})
export class MyView {
    @Input() model: IView;

    previousComponent: ComponentRef;

    constructor(private loader: DynamicComponentLoader, private element: ElementRef) {
    }

    onChanges(changes: {[key: string]: SimpleChange}) {
        var modelChanges = changes['model']
        if (modelChanges) {
            var model = modelChanges.currentValue;
            @Component({
                selector: 'viewRenderer',
                template: model.template,
            })
            class ViewRenderer {
                model: any;
            }
            if (this.previousComponent) {
                this.previousComponent.dispose();
            }
            this.loader.loadIntoLocation(ViewRenderer, this.element, 'attach')
                .then(component => {
                    component.instance.model = model;
                    this.previousComponent = component;
                });
        }
    }
}

Usou algo como isto:

@Component({
    selector: 'app',
    template: `
        <view [model]='currentView'></view>
        <button (click)='changeView()'>Change View</button>
    `,
    directives: [MyView]
})
export class App {
    currentView: IView = new SalesView();
    changeView() {
        this.currentView = new CalendarView();
    }
}

EDIT: Thisteve problemas que foram corrigidos agora.

O problema restante é que ele cria vários elementos DOM aninhados desnecessários. O que eu realmente quero é:

<view>VIEW CONTENTS RENDERED HERE</view>

Em vez disso, temos:

<view>
      <span></spawn>
      <viewrenderer>VIEW CONTENTS RENDERED HERE</viewrenderer>
</view>

Isso piora quanto mais visualizações aninhamos, sem metade das linhas aqui sendo uma porcaria estranha:

<view>
    <span></spawn>
    <viewrenderer>
        <h1>CONTENT</h1>
        <view>
            <span></spawn>
            <viewrenderer>
                <h1>NESTED CONTENT</h1>
                <view>
                    <span></spawn>
                    <viewrenderer>
                        <h1>NESTED NESTED CONTENT</h1>
                    </viewrenderer>
                </view>
            </viewrenderer>
        </view>
    </viewrenderer>
    <viewrenderer>
        <h1>MORE CONTENT</h1>
        <view>
            <span></spawn>
            <viewrenderer>
                <h1>CONTENT</h1>
            </viewrenderer>
        </view>
    </viewrenderer>
</view>

questionAnswers(2)

yourAnswerToTheQuestion