将模型和模板动态绑定到Angular 2中的DOM节点 [英] Dynamically bind model and template to at DOM node in Angular 2

查看:42
本文介绍了将模型和模板动态绑定到Angular 2中的DOM节点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

柱塞定义了一个<view>组件,该组件可以呈现任意模型+模板.需要将其更改为替换以前呈现的内容,而不是附加新的对等项.

This Plunker defines a <view> component which can render an arbitrary model+template. This needs to be changed to replace the previously rendered contents rather than appending new peers.

这要感谢用户3636086的响应.

This is working now, thanks to the response by user3636086.

一个问题仍然存在:与Angular 1不同,Angular 2迫使我创建一个嵌套组件来更新模板(因为模板实际上是组件 class 的静态属性),所以我有一个一堆不必要的DOM节点被添加.

One problem still remains: unlike Angular 1, Angular 2 forces me to create a nested component to update a template (since templates are effectively a static property of a component's class), so I have a bunch of unnecessary DOM nodes being added.

在我们的项目中,我们希望代码中的 most 不直接依赖UI框架.我们有一个viewmodel类,它将模型和视图联系在一起.以下是简化的示例:

In our project we'd prefer most of our code to have no direct dependency on a UI framework. We have a viewmodel class which ties together a model and view. Here are simplified examples:

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指令,可以显示以下视图之一:

We have a view directive that can display one of these views:

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

如果viewInstance发生更改,则会在DOM中的该位置呈现一个新的View对象(模型+模板).例如,此仪表板"视图可以具有可以呈现的任意视图列表:

If viewInstance changes, a new View object is rendered (model + template) at that location in the DOM. For instance, this Dashboard view can have an arbitrary list of views that it can render:

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>,依此类推.

A crucial point is that this is composable. The <view> can contain a <view> which can contain a <view>, so on and so forth.

在Angular 1中,我们的view指令如下所示:

In Angular 1, our view directive looks something like this:

.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位置动态加载新模板非常笨拙,迫使我每次都要创建一个新的组件类型.

Angular 2

I'm trying to port this to Angular 2, but dynamically loading a new template at a DOM location is very clunky, forcing me to create a new component type every time.

这是我想出的最好的方法(根据user3636086的反馈进行了更新):

This is the best I've come up with (updated with feedback from 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();
    }
}

现在已经解决了这个 问题.

This had problems that have now been fixed.

剩下的问题是它创建了一堆不必要的嵌套DOM元素.我真正想要的是:

The remaining problem is that it creates a bunch of unnecessary nested DOM elements. What I really want is:

<view>VIEW CONTENTS RENDERED HERE</view>

相反,我们有:

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

随着我们嵌套更多的视图,情况变得更糟,没有一半的行变得多余:

This gets worse the more views we have nested, without half the lines here being extraneous crap:

<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 (最近的评论,而不是原始问题)

see https://github.com/angular/angular/issues/2753 (the recent comments, not the original issue)

长版

我有一个类似的用例,并且一直在关注有关推荐方法的讨论.

I have a similar use-case and have been keeping an eye on chatter about recommended approaches to it.

到目前为止, DynamicComponentLoader 确实是实际的动态组件编译工具(阅读:$compile的替代品),而您在示例中采用的方法与,@ RobWormald发布,以回应几个类似的问题关于gitter .

As of now, DynamicComponentLoader is indeed the de-facto tool for dynamic component compilation (read: stand-in for $compile) and the approach you've taken in your example is essentially identical to this one, which @RobWormald has posted in response to several similar questions on gitter.

这是另一个有趣的示例 @EricMartinez使用非常相似的方法给了我.

Here's another interesting example @EricMartinez gave me, using a very similar approach.

但是,是的,这种方法对我来说也很笨拙,我还没有找到(或想出)使用DCL进行此操作的更优雅的方法.我在上面链接的 github问题上的评论包含它的第三个示例,以及类似的示例迄今为止的批评都没有得到答复.

But yes, this approach feels clunky to me too, and I've yet to find (or come up with) a more elegant way of doing this with DCL. The comments on the github issue I linked above contain a third example of it, along with similar criticisms that have so far gone unsanswered.

我很难相信这样一个通用的用例的规范解决方案在最终版本中会很笨拙(特别是给 relative 的优雅),但是除此之外的任何事情都是猜测.

I have a hard time believing that the canonical solution for a use-case as common as this will be so clunky in the final release (particularly given then relative elegance of $compile), but anything beyond that would be speculation.

如果您在gitter线程中grep"DCL"或"DynamicComponentLoader",那里有一些关于此主题的有趣的对话.一位核心团队成员说:"DCL是一种动力工具,我们只希望将其用于真正从事框架工作的人即可使用"-我发现……很有趣.

If you grep "DCL" or "DynamicComponentLoader" in the gitter thread, there are several interesting conversations on this topic there. One of the core team guys said something to the effect of "DCL is a power-tool that we only expect will be used by people doing really framework-ey things" - which I found... interesting.

(如果gitter的搜索没有吸引的话,我会直接引用/链接到它)

(I'd have quoted/linked to that directly if gitter's search didn't suck)

这篇关于将模型和模板动态绑定到Angular 2中的DOM节点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆