簡體   English   中英

將模型和模板動態綁定到Angular 2中的DOM節點

[英]Dynamically bind model and template to at DOM node in Angular 2

精簡版

這個Plunker定義了一個<view>組件,它可以渲染任意模型+模板。 這需要更改以替換先前呈現的內容,而不是添加新的對等項。

編輯:由於user3636086的響應,現在正在運行。

仍然存在一個問題:與Angular 1不同,Angular 2強迫我創建一個嵌套組件來更新模板(因為模板實際上是組件的靜態屬性),所以我添加了一堆不必要的DOM節點。


長版

角度1

在我們的項目中,我們更喜歡我們的大部分代碼都沒有直接依賴於UI框架。 我們有一個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更改,則在DOM中的該位置呈現新的View對象(模型+模板)。 例如,此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());
                });
            }
        };
    }
]);

Angular 2

我正在嘗試將其移植到Angular 2,但是在DOM位置動態加載新模板非常笨重,迫使我每次都要創建一個新的組件類型。

這是我提出的最好的(更新來自user3636086的反饋):

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

簡潔版本

請參閱https://github.com/angular/angular/issues/2753 (最近的評論,不是原始問題)


長版

我有一個類似的用例,並一直關注有關推薦方法的討論。

截至目前, DynamicComponentLoader確實是動態組件編譯事實上的工具(閱讀:替身$compile ),你已經邁出你的榜樣的做法是基本相同的這一個 ,這@RobWormald發布了響應關於gitter的幾個類似的問題。

這是另一個有趣的例子 @EricMartinez使用非常類似的方法給了我。

但是,是的,這種方法對我來說也很笨拙,而且我還沒有找到(或想出)一種更優雅的方式來做這個用DCL。 我上面鏈接的關於github問題的評論包含了它的第三個例子,以及到目前為止尚未得到答復的類似批評。

我很難相信在最終版本中這個常見的用例的規范解決方案會非常笨重(特別是考慮到當時相對優雅的$compile ),但除此之外的任何事情都是猜測。

如果你在gitter線程中grep“DCL”或“DynamicComponentLoader”,那么就這個主題有幾個有趣的對話。 其中一個核心團隊成員說“DCL是一種我們唯一期望的人們會用到真正構建框架的電源工具” - 我發現......很有趣。

(如果gitter的搜索沒有吮吸,我會直接引用/鏈接到那個)

通過代碼中的微小更改可以實現正確的行為:在添加新組件之前,必須“先處理”先前創建的組件。

savedComp: Component = null;
...
if (this.savedComp) {
  this.savedComp.dispose();
}
this.loader.loadIntoLocation(DynamicComponent, this.element, 'attach')
    then((res) => {res.instance.model = model; this.savedComp = res;});

這里有完整的解決方案: http//plnkr.co/edit/KQM31HTubn0LfL7jSP5l

希望能幫助到你!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM