Динамически привязывать модель и шаблон к узлу DOM в Angular 2

Укороченная версия

этоPlunker определяет<view> компонент, который может визуализировать произвольную модель + шаблон. Это должно быть изменено назамещать ранее визуализированное содержимое, а не добавление новых пиров.

РЕДАКТИРОВАТЬ: Это работает сейчас, благодаря ответу пользователя 3636086.

Одна проблема все еще остается: в отличие от Angular 1, Angular 2 вынуждает меня создавать вложенный компонент для обновления шаблона (поскольку шаблоны фактически являются статическим свойством компонента).учебный класс), поэтому у меня есть куча ненужных узлов DOM.

Длинная версияУгловой 1

В нашем проекте мы бы предпочлисамый нашего кода, чтобы не иметь прямой зависимости от инфраструктуры пользовательского интерфейса. У нас есть класс viewmodel, который связывает воедино модель и представление. Вот упрощенные примеры:

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>`; 
    }
}

У нас естьview директива, которая может отображать одно из следующих представлений:

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

ЕслиviewInstance изменения, новый объект View отображается (модель + шаблон) в этом месте в DOM. Например, это представление Dashboard может иметь произвольный список представлений, которые он может отображать:

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

Важным моментом является то, что это составно.<view> может содержать<view> который может содержать<view>, так далее и так далее.

В Angular 1 нашview Директива выглядит примерно так:

.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());
                });
            }
        };
    }
]);
Угловой 2

Я пытаюсь перенести это на Angular 2, но динамическая загрузка нового шаблона в DOM-месте очень неуклюжа, заставляя меня каждый раз создавать новый тип компонента.

Это лучшее, что я придумал (дополнено отзывами от пользователя 3636086):

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

Используется что-то вроде этого:

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

РЕДАКТИРОВАТЬ: Этоимел проблемы, которые сейчас исправлены.

Оставшаяся проблема заключается в том, что он создает кучу ненужных вложенных элементов DOM. Что я действительно хочу это:

<view>VIEW CONTENTS RENDERED HERE</view>

Вместо этого мы имеем:

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

Это тем хуже, чем больше мы вкладываем представлений, при этом половина строк здесь не является посторонним дерьмом:

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

Ответы на вопрос(2)

Ваш ответ на вопрос