什么是angular的结构型指令,结构型指令怎么配合ViewContainerRef和TemplateRef使用 *?

结构型指令:
结构型指令的职责是 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

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值