使用 Angular 4 / 5 动态填充模板
Dynamically populating a template with Angular 4 / 5
我们的应用程序布局具有工具栏和实际内容区域。由于工具栏的结构有些复杂 DOM 我们决定从中创建一个组件。
我们可以在每个页面上重用这个组件,但工具栏位置实际上不在内容旁边(其他一些组件介于两者之间)。所以我们宁愿在父页面组件中放置一次工具栏,并根据需要在每个页面上声明工具栏内容。
<app>
<toolbar>
<ng-container #toolbar></ng-container>
</toolbar>
<some unrelated component>...</some unrelated component>
<content>
<ng-template #tbcontent>
<button>Click me!</button>
</ng-template>
<p>Actual page content!</p>
</content>
</app>
因此在上面的示例中,我们会将 #tbcontent
中的内容放入 #toolbar
中,有效地呈现如下内容:
<app>
<toolbar>
<button>Click me!</button>
</toolbar>
<some unrelated component>...</some unrelated component>
<content>
<p>Actual page content!</p>
</content>
</app>
我们已尝试使用 @ViewChild
从根 AppComponent
查询 #toolbar
和 #tbcontent
节点(为清楚起见,已简化代码):
@Component({
/* ... */
})
export class AppComponent implements OnInit, AfterViewInit {
@ViewChild("tbcontent") tbcontent: TemplateRef<any>;
@ViewChild("toolbar", {read: ViewContainerRef}) toolbar: ViewContainerRef;
/* ... */
ngAfterViewInit() {
console.log('ngAfterViewInit');
console.log(this.tbcontent);
console.log(this.toolbar);
this.toolbar.createEmbeddedView(this.tbcontent);
}
}
但是两个引用都是undefined
。有没有办法正确访问元素?这实际上是一种不好的做法吗,也许有更好的方法来达到相同的结果?
更新: @martin-nuc 友善地建议使用 *ngTemplateOutlet
,这似乎可以解决问题,但只有当被复制的节点位于同一节点时零件。这是一个 JSFiddle 显示这个:
https://jsfiddle.net/carlosafonso/k3oq56oq/3/
谢谢。
当您定义模板时,它不会插入到 DOM 中。您需要使用一些指令将其插入到 DOM.
此外,ng-container
基本上只是一些内容的占位符,但不会呈现为 DOM。
我认为你想做的是
<toolbar>
<ng-container *ngTemplateOutlet="tbcontent"></ng-container>
</toolbar>
编辑:问题是如何在父组件中显示子组件的模板。我基本上将模板作为子组件的输出传递。这是实现它的方法。
父组件:
@Component({
selector: 'app',
template: `
<div class="container">
<toolbar>
<ng-container *ngTemplateOutlet="toolbarContent"></ng-container>
</toolbar>
<hr/>
<content (toolbarLoaded)="displayToolbar($event)"></content>
</div>
`,
})
class AppComponent {
constructor() {
}
displayToolbar($event) {
this.toolbarContent = $event;
}
}
带有工具栏模板的子组件:
@Component({
selector: 'content',
template: `
<ng-template #tb>
<button type="button" class="btn btn-sm btn-primary">Content-specific action</button>
</ng-template>
<p>These are the page contents.</p>
`
})
class ContentComponent {
@Output() toolbarLoaded = new EventEmitter<any>();
@ViewChild('tb') tb;
ngAfterViewInit() {
// arrow function dont work in jsfiddle somehow :(
let that = this;
// we need timeout to avoid Expression had changed after it was checked (https://github.com/angular/angular/issues/6005)
setTimeout(() => {
that.toolbarLoaded.emit(that.tb);
}, 0);
}
}
Jsfiddle:https://jsfiddle.net/fdv1tesr/3/
我们的应用程序布局具有工具栏和实际内容区域。由于工具栏的结构有些复杂 DOM 我们决定从中创建一个组件。
我们可以在每个页面上重用这个组件,但工具栏位置实际上不在内容旁边(其他一些组件介于两者之间)。所以我们宁愿在父页面组件中放置一次工具栏,并根据需要在每个页面上声明工具栏内容。
<app>
<toolbar>
<ng-container #toolbar></ng-container>
</toolbar>
<some unrelated component>...</some unrelated component>
<content>
<ng-template #tbcontent>
<button>Click me!</button>
</ng-template>
<p>Actual page content!</p>
</content>
</app>
因此在上面的示例中,我们会将 #tbcontent
中的内容放入 #toolbar
中,有效地呈现如下内容:
<app>
<toolbar>
<button>Click me!</button>
</toolbar>
<some unrelated component>...</some unrelated component>
<content>
<p>Actual page content!</p>
</content>
</app>
我们已尝试使用 @ViewChild
从根 AppComponent
查询 #toolbar
和 #tbcontent
节点(为清楚起见,已简化代码):
@Component({
/* ... */
})
export class AppComponent implements OnInit, AfterViewInit {
@ViewChild("tbcontent") tbcontent: TemplateRef<any>;
@ViewChild("toolbar", {read: ViewContainerRef}) toolbar: ViewContainerRef;
/* ... */
ngAfterViewInit() {
console.log('ngAfterViewInit');
console.log(this.tbcontent);
console.log(this.toolbar);
this.toolbar.createEmbeddedView(this.tbcontent);
}
}
但是两个引用都是undefined
。有没有办法正确访问元素?这实际上是一种不好的做法吗,也许有更好的方法来达到相同的结果?
更新: @martin-nuc 友善地建议使用 *ngTemplateOutlet
,这似乎可以解决问题,但只有当被复制的节点位于同一节点时零件。这是一个 JSFiddle 显示这个:
https://jsfiddle.net/carlosafonso/k3oq56oq/3/
谢谢。
当您定义模板时,它不会插入到 DOM 中。您需要使用一些指令将其插入到 DOM.
此外,ng-container
基本上只是一些内容的占位符,但不会呈现为 DOM。
我认为你想做的是
<toolbar>
<ng-container *ngTemplateOutlet="tbcontent"></ng-container>
</toolbar>
编辑:问题是如何在父组件中显示子组件的模板。我基本上将模板作为子组件的输出传递。这是实现它的方法。
父组件:
@Component({
selector: 'app',
template: `
<div class="container">
<toolbar>
<ng-container *ngTemplateOutlet="toolbarContent"></ng-container>
</toolbar>
<hr/>
<content (toolbarLoaded)="displayToolbar($event)"></content>
</div>
`,
})
class AppComponent {
constructor() {
}
displayToolbar($event) {
this.toolbarContent = $event;
}
}
带有工具栏模板的子组件:
@Component({
selector: 'content',
template: `
<ng-template #tb>
<button type="button" class="btn btn-sm btn-primary">Content-specific action</button>
</ng-template>
<p>These are the page contents.</p>
`
})
class ContentComponent {
@Output() toolbarLoaded = new EventEmitter<any>();
@ViewChild('tb') tb;
ngAfterViewInit() {
// arrow function dont work in jsfiddle somehow :(
let that = this;
// we need timeout to avoid Expression had changed after it was checked (https://github.com/angular/angular/issues/6005)
setTimeout(() => {
that.toolbarLoaded.emit(that.tb);
}, 0);
}
}
Jsfiddle:https://jsfiddle.net/fdv1tesr/3/