Enlace y plantilla dinámicamente al nodo DOM en Angular 2

Version corta

EstaSaqueador define un<view> componente que puede representar un modelo arbitrario + plantilla. Esto necesita ser cambiado areemplazar los contenidos representados anteriormente en lugar de agregar nuevos pares.

EDITAR: Esto está funcionando ahora, gracias a la respuesta del usuario3636086.

Todavía queda un problema: a diferencia de Angular 1, Angular 2 me obliga a crear un componente anidado para actualizar una plantilla (ya que las plantillas son efectivamente una propiedad estática de un componenteclase), por lo que tengo un montón de nodos DOM innecesarios que se agregan.

Versión largaAngular 1

En nuestro proyecto preferiríamosmás de nuestro código para no tener una dependencia directa en un marco de IU. Tenemos una clase viewmodel que une un modelo y una vista. Aquí hay ejemplos 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>`; 
    }
}

Tenemos unaview directiva que puede mostrar una de estas vistas:

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

SiviewInstance cambios, se representa un nuevo objeto Ver (modelo + plantilla) en esa ubicación en el DOM. Por ejemplo, esta vista de Panel puede tener una lista arbitraria de vistas que puede representar:

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'>"; }
}

Un punto crucial es que esto es composable. los<view> puede contener un<view> que puede contener un<view>, y así sucesivamente.

En Angular 1, nuestroview La directiva se parece a esto:

.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

Estoy tratando de portar esto a Angular 2, pero cargar dinámicamente una nueva plantilla en una ubicación DOM es muy complicado, lo que me obliga a crear un nuevo tipo de componente cada vez.

Esto es lo mejor que se me ocurrió (actualizado con los comentarios del usuario3636086):

@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;
                });
        }
    }
}

Usó algo como esto:

@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();
    }
}

Edita estotenía problemas que ahora se han solucionado.

El problema restante es que crea un montón de elementos DOM anidados innecesarios. Lo que realmente quiero es:

<view>VIEW CONTENTS RENDERED HERE</view>

En cambio tenemos:

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

Esto empeora a medida que más vistas hemos anidado, sin que la mitad de las líneas aquí sean basura extraña:

<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>

Respuestas a la pregunta(2)

Su respuesta a la pregunta