此Plunker定义了一个<view>
组件,可以呈现任意模型+模板。这需要更改为替换以前呈现的内容,而不是追加新的对等。
编辑:由于user3636086的回复,现在正在运行。
仍然存在一个问题:与Angular 1不同,Angular 2强迫我创建一个嵌套组件来更新模板(因为模板实际上是组件的类的静态属性),所以我有一个一堆不必要的DOM节点被添加。
在我们的项目中,我们更喜欢我们的代码,而不是直接依赖于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对象(模型+模板)。例如,此仪表板视图可以具有可以呈现的任意视图列表:
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,但是在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>
答案 0 :(得分:3)
简短版
请参阅https://github.com/angular/angular/issues/2753(最近的评论,而不是原始问题)
长版
我有一个类似的用例,并一直关注有关推荐方法的讨论。
截至目前,DynamicComponentLoader确实是动态组件编译的事实上的工具(阅读:$compile
的替身),您在示例中采用的方法基本上与this one,@ RobWormald针对几个类似的问题on gitter发布了这些问题。
这里是another interesting example @EricMartinez使用非常类似的方法给了我的。
但是,是的,这种方法对我来说也很笨拙,而且我还没有找到(或想出)用DCL做到这一点的更优雅方式。关于github issue I linked above的评论包含了它的第三个例子,以及到目前为止尚未得到答复的类似批评。
我很难相信在最终版本中,这个常见用例的规范解决方案会如此笨拙(特别是当时相对优雅的$compile
) ,但除此之外的任何事情都是猜测。
如果你在gitter线程中grep“DCL”或“DynamicComponentLoader”,那么在这个主题上有几个有趣的对话。其中一个核心团队成员说“DCL是一种我们唯一期望的人们会用到真正构建框架的电源工具” - 我发现......很有趣。
(如果gitter的搜索没有吮吸,我会直接引用/链接到这一点)
答案 1 :(得分:2)
只需对代码进行微小更改即可实现正确的行为:您必须这样做 在添加新组件之前“先处理”先前创建的组件。
cmpl
完整解决方案: http://plnkr.co/edit/KQM31HTubn0LfL7jSP5l
希望它有所帮助!