[英]Angular: Can I pass values from the console to a component like how a parent component passes a value to its child?
[英]how can I transclude child component template into its parent in Angular 6+?
回到AngularJS,我创建了一个简单的智能表组件,可用于我们的团队:
<smart-table items="vm.people">
<column field="name">{{row.firstname}} {{row.lastname}}</column>
<column field="age">{{vm.getAge(row.birthday)}}</column>
</smart-table>
这是一个人为的示例,但是它像这样工作。 它生成带有标题的表,并正确使用<column>
标记的内部内容作为每个单元格 ( <td>
元素)的模板。
现在,我试图将其移植到Angular(6+)。 到目前为止,使用@ContentChildren
可以轻松提取列列表。
import { Component, OnInit, Input, ContentChildren, QueryList } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<app-table [data]="data">
<app-column title="name">{{name}}</app-column>
<app-column title="age">{{birthday}}</app-column>
</app-table>
`,
})
export class AppComponent {
data = [{
name: 'Lorem Ipsum',
birthday: new Date(1980, 1, 23),
}, {
name: 'John Smith',
birthday: new Date(1990, 4, 5),
}, {
name: 'Jane Doe',
birthday: new Date(2000, 6, 7),
}];
}
@Component({
selector: 'app-column',
template: ``,
})
export class ColumnComponent implements OnInit {
@Input() title: string;
constructor() { }
ngOnInit() {
}
}
@Component({
selector: 'app-table',
template: `
<table>
<thead>
<th *ngFor="let column of columns">{{column.title}}</th>
</thead>
<tbody>
<tr *ngFor="let row of data">
<td *ngFor="let column of columns">
<!-- <ng-container *ngTemplateOutlet="column.title;context:row" ></ng-container> -->
</td>
</tr>
</tbody>
</table>
`,
})
export class TableComponent implements OnInit {
@Input() data: any[];
@ContentChildren(ColumnComponent) columns: QueryList<ColumnComponent>;
constructor() { }
ngOnInit() {
}
}
这将呈现以下HTML:
<table>
<thead>
<!--bindings={"ng-reflect-ng-for-of": "[object Object],[object Object"}-->
<th>name</th>
<th>age</th>
</thead>
<tbody>
<!--bindings={"ng-reflect-ng-for-of": "[object Object],[object Object"}-->
<tr>
<!--bindings={"ng-reflect-ng-for-of": "[object Object],[object Object"}-->
<td></td>
<td></td>
</tr>
<tr>
<!--bindings={"ng-reflect-ng-for-of": "[object Object],[object Object"}-->
<td></td>
<td></td>
</tr>
<tr>
<!--bindings={"ng-reflect-ng-for-of": "[object Object],[object Object"}-->
<td></td>
<td></td>
</tr>
</tbody>
</table>
但是现在我被困在尝试将 <app-column>
组件的内容插入到<app-table>
模板中。 我在这里已经阅读了几个答案( 这个和这个 )。 但是我的问题是,要么有一组静态模板,要么将它们插入静态位置。 就我而言,我需要在运行时在*ngFor
中使用该模板。
在AngularJS中,我使用以下代码:
function compile(tElement) {
const columnElements = tElement.find('column');
const columns = columnElements.toArray()
.map(cEl => ({
field: cEl.attributes.field.value,
header: cEl.attributes.header.value,
}));
const template = angular.element(require('./smart-table.directive.html'));
tElement.append(template);
// The core of the functionality here is that we generate <td> elements and set their
// content to the exact content of the "smart column" element.
// during the linking phase, we actually compile the whole template causing those <td> elements
// to be compiled within the scope of the ng-repeat.
const tr = template.find('tr[ng-repeat-start]');
columnElements.toArray()
.forEach(cEl => {
const td = angular.element('<td/>')
.html(cEl.innerHTML);
tr.append(td);
});
const compile = $compile(template);
// comment out originals
columnElements.wrap(function () {
return `<!-- ${this.outerHTML} -->`;
});
return function smartTableLink($scope) {
$scope.vm.columns = columns;
compile($scope);
};
}
在Angular中,内容投影和模板实例化是分开的。 也就是说,内容投影会投影现有节点,您将需要一个结构指令来按需创建节点。
也就是说, column
需要成为一个结构指令,它将其模板传递给父smart-table
,然后该smart-table
可以根据需要将其添加到其ViewContainer
中。
这是我最后得到的,这要归功于Meriton的指示。
正如他建议的那样,我将我的app-column
组件改为了指令 。 该指令必须出现在ng-template
元素上:
@Directive({
selector: 'ng-template[app-column]',
})
export class ColumnDirective {
@Input() title: string;
@ContentChild(TemplateRef) template: TemplateRef<any>;
}
app-table
组件变为:
@Component({
selector: 'app-table',
template: `
<table>
<thead>
<th *ngFor="let column of columns">{{column.title}}</th>
</thead>
<tbody>
<tr *ngFor="let row of data">
<td *ngFor="let column of columns">
<ng-container
[ngTemplateOutlet]="column.template"
[ngTemplateOutletContext]="{$implicit:row}">
</ng-container>
</td>
</tr>
</tbody>
</table>
`,
})
export class TableComponent {
@Input() data: any[];
@ContentChildren(ColumnDirective) columns: QueryList<ColumnDirective>;
}
起初,由于不得不将ng-template
暴露给一些队友(还不是非常精通Angular的团队...)而使我有些沮丧,但由于一些原因,我最终还是喜欢这种方法。 首先,代码仍然易于阅读:
<app-table [data]="data">
<ng-template app-column title="name" let-person>{{person.name}}</ng-template>
<ng-template app-column title="age" let-person>{{person.birthday}}</ng-template>
</app-table>
其次,由于我们必须指定上面模板的上下文的变量名称( let-person
),因此它允许我们保留业务上下文。 因此,如果现在要谈论动物而不是人(在另一页上),我们可以轻松地编写:
<app-table [data]="data">
<ng-template app-column title="name" let-animal>{{animal.name}}</ng-template>
<ng-template app-column title="age" let-animal>{{animal.birthday}}</ng-template>
</app-table>
最后,该实现完全保留了对父组件的访问。 因此,假设我们在app-root
组件上添加了一个year()
方法来从生日中提取年份,我们可以像这样使用它:
<app-table [data]="data">
<ng-template app-column title="name" let-person>{{person.name}}</ng-template>
<ng-template app-column title="year" let-person>{{year(person.birthday)}}</ng-template>
</app-table>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.