结构型指令:
结构型指令的职责是 HTML 布局。 它们塑造或重塑 DOM 的结构,比如添加、移除或维护这些元素。
像其它指令一样,你可以把结构型指令应用到一个宿主元素上。 然后它就可以对宿主元素及其子元素做点什么。
结构型指令非常容易识别。 星号(*)被放在指令的属性名之前
星号是“句法糖”,代表更复杂的内容。Angular 把 *ngIf
属性 翻译成一个 <ng-template>
元素,*ngIf
在内部,是下面这样的
<div *ngIf="hero" class="name">{{hero.name}}</div>
等价于
<ng-template [ngIf]="hero">
<div class="name">{{hero.name}}</div>
</ng-template>
下表演示了Angular如何简化微语法
微语法 | 脱糖的 |
---|---|
*ngFor="let item of [1,2,3]" | <ng-template ngFor let-item [ngForOf]="[1,2,3]"> |
*ngFor="let item of [1,2,3] as items; trackBy: myTrack; index as i" | <ng-template ngFor let-item [ngForOf]="[1,2,3]" let-items="ngForOf" [ngForTrackBy]="myTrack" let-i="index"> |
*ngIf="exp" | <ng-template [ngIf]="exp"> |
*ngIf="exp as value" | <ng-template [ngIf]="exp" let-value="ngIf"> |
为了深刻理解,下面来做小例子,
下面这个例子是利用结构型指令fjtreeDel在标签上设置一个item变量获取从指令内部返回的值data
<div *fjtreeDel="let item = data">姓名:{{item}}</div>
fjtreeDel指令代码
@Directive({
selector: '[fjtreeDel]',
})
export class FjtreeDelDirective{
// 获取当前标签下的Template,然后填充到视图viewContainer中,并设置data数据
constructor(private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef) {
this.viewContainer.createEmbeddedView(this.templateRef, {data: '我是fjStructure'});
}
}
渲染大浏览器中就可以看到,在指令内部设置的data数据,在外面可以获取到
接下来我们进入正题,优化这个功能结合ViewContainerRef和TemplateRef
做一个用户在外层传入数据,在内层使用指令获取
类似下面这样的代码
<fj-tree [dataSoure]="dataSoure">
<div *fjTreeNode="let name=data">姓名:{{name}}</div>
</fj-tree>
此时我们需要新建一个ViewContainerRef容器指令,放在fj-tree组件上
// 组件中的容器,用来填充fjtreeDel试图,createEmbeddedView(templateRef:TemplateRef<C>, context?:C,index?:number)
@Directive({
selector: '[fjtreeOutlet]'
})
export class FjtreeOutletDirectve {
constructor(public viewContainer: ViewContainerRef) {}
}
然后建新建指令fjTreeNode
1.定义静态变量mostRecentTreeNode,并指向知己,别的组件调用此方法来更改data
2.内嵌模板,可以使用TemplateRef取得 <ng-template>
的内容,微语法有不懂的可以看上面详解
<div *fjTreeNode="node" class='nav'>aaaa</div>
等价于
<ng-template [fjTreeNode]="node">
<div class='nav'>anavnavnav</div>
</ng-template>
@Directive({
selector: '[fjTreeNode]'
})
export class FjtreenodeDirective implements OnDestroy {
static mostRecentTreeNode: FjtreenodeDirective | null = null;
set data(val) {this._data = val;}
get data() {return this._data;}
private _data = '';
constructor(public template: TemplateRef<any>) {
FjtreenodeDirective.mostRecentTreeNode = this as FjtreenodeDirective;
}
}
然后建新建组件fj-tree
1.这个组件是一个视图容器,本身并没有别的内容
* fjtreeOutlet<ViewContainerRef>组件中的容器,用来填充试图 createEmbeddedView(template)
* ng-container 渲染所包含的模板内容,不包含自身
* 所以最终呈现的视图是fjtreeOutlet容器中的内容
2.@ViewChild(FjtreeOutletDirectve,{...})方法,会在视图的DOM 中查找能匹配上该FjtreeOutletDirectve的第一个元素或指令
3. @ContentChildren QueryList方法 从模板获取元素或指令,例如下面的代码,可以得到两套pane数据
<fj-tree>
<pane/>
<pane/>
</fj-tree>
@Component({
selector: 'fj-tree',
template: '<ng-container fjtreeOutlet></ng-container>'
})
export class FjTreeComponent implements AfterContentInit {
// 当前组件的fjtreeOutlet
@ViewChild(FjtreeOutletDirectve, {static: true}) outlet: FjtreeOutletDirectve;
// 当前组件里面所有的fjtreeDel标签里面数据,
@ContentChildren(FjtreenodeDirective, {descendants: true}) Def: QueryList<FjtreenodeDirective>;
}
好了,框架已经搭好了,接下来设置元数据以及出口数据
class FjTreeComponent里面添加如下代码:
@Input()
set dataSoure(value) {if (!value) return;this._dataSoure = value;}
get dataSoure() {return this._dataSoure;}
_dataSoure: [];
insertNode(dataSoure) {
dataSoure.forEach((item, index) => {
// 将Def填充到视图outlet
this.outlet.viewContainer.createEmbeddedView(this.Def.first.template, {data: item});
});
FjtreenodeDirective.mostRecentTreeNode.data = dataSoure;
}
ngAfterContentInit() {this.insertNode(this.dataSoure);}
到了这里,功能算是做完了,
在项目里面使用:
<fj-tree [dataSoure]="dataSoure">
<div *fjTreeNode="let item=data">姓名:{{item.username}}</div>
</fj-tree>
dataSoure = [
{'id': '1', 'username': '貂蝉', 'MobilePhone': '15117999999', 'age': 16},
{'id': '2', 'username': '吕布', 'MobilePhone': '18032556552', 'age': 24},
{'id': '3', 'username': '王昭君', 'MobilePhone': '18032556554', 'age': 1},
{'id': '4', 'username': '小乔', 'MobilePhone': '18032556555', 'age': 1}
];
浏览器渲染:
总结:
[fjTreeNode]用来导出了数据对象data
。在模板中使用了它<div>{{ item.username }}</div>
。
[fjtreeOutlet]用来在fj-tree模板中指令设置了子节点的出口
本例子需要掌握的知识点:
1.结构型指令
2.@ContentChildren
3.@ViewChild
4.ViewContainerRef
5.TemplateRef