ng-template、ng-content、ng-container

1,415 阅读3分钟
不知道怎么同步quiver笔记到手机端,决定把之前写的笔记发到这来,一篇篇搬吧 -_-

  • ng-template

    使用antd组件库时常常用到,用于定义HTML模板,定义后不会直接显示,需要通过其他结构型指令(例如 ngIf )或者 template-ref 渲染到页面上。

    • 使用 ngIf 渲染
    <div *ngIf="isShow else testTemp">
      this is a html ! (isShow为true时展示)
    </div>
    <!-- #testTemp 起的模板名称, 要求是当前的component里唯一 -->
    <ng-template #testTemp>
      this is a templateHtml !
    </ng-template>
    
    • TemplateRefViewContainerRef 渲染. TemplateRef 对应 ng-template 的引用; ViewContainerRef 是容器View的引用,用来操作DOM;
    @Component({
      selector: 'app-template',
      templateUrl: `
      <ng-template #testTemp>
        this is a templateHtml !
      </ng-template>
      `,
      styleUrls: ['./app-template.component.less'],
    })
    export class TemplateComponent {
      @ViewChild('testTemp')
      testTemp: TemplateRef<any>;
      constructor(private vcRef: ViewContainerRef) {
      }
    
      ngAfterViewInit() {
        // 这样testTemp对应的ng-template内容就显示出来了
        this.vcRef.createEmbeddedView(this.testTemp);
      }
    }
    
    • 使用 ngTemplateOutlet 渲染模板。ps: 在渲染页面之前,Angular 会把及其内容替换为注释。
    <!-- testTemp 可以是组件声明的 TemplateRef 类型的模板对象;
          也可以直接是同一页面里的ng-template标签声明的名称  -->
    <div *ngTemplateOutlet="testTemp"></div>
    
    • 了解下 ngTemplateOutlet 功能: 支持上下文传参,context;
      <!--  xx是在ng-template内部使用的变量名字,xx对应的值是上下文对象yy属性的值 -->
      <ng-template #testTemp let-xx="yy">
        this is a templateHtml ! {{ xx }}
      </ng-template>
      <!-- testContext 是json对象 -->
      <div *ngTemplateOutlet="testTemp; context: testContext"></div>
    
  • ng-container

    这个ng-container 既不是Component 也不是 Directive,书写时就是个html标签,但是实际页面不会生成任何元素,一般都用作逻辑处理。

    • 使用场景 ngFor遍历元素时加入ngIf 控制显示。ngFor和ngIf不能同时使用这个时候就需要使用ng-container了,例如:
    <ng-container *ngFor="let item of list;let i = index">
      <div *ngIf="i % 2 == 0">
        {{ item }} - {{i}}
      </div>
    </ng-container>
    
  • ng-content

    ng-content 这个标签的功能和 router-outlet 类似,都像一个占位标签。 先回顾下 router-outlet 使用方式:出现该标签时,对应的路由配置中会有 children 属性,当触发路由时,页面会替换到router-outlet的位置。 ng-content和router-outlet功能类似,ng-content 出现在组件中时,引入该组件的父组件可以替换掉ng-content。

    <!-- 子组件 test-child.html 页面 -->
    <div>
      这是个子组件:
      <ng-content></ng-content>
    </div>
    
    <!-- 父组件页面 parent.html 引用时 -->
    <test-child>
      你说的对!
    </test-child>
    

    最终执行完效果: 看到这,大概懂了这东西是干嘛的吧? (和Vue的slot有点类似) 当然,这个标签不可能在一个组件里只出现一次,下面看下当一个页面里出现多个ng-content怎么使用。ng-content中有个属性 select 可以让你在你想要的位置投射。这个属性支持css选择器、标签选择器、属性选择器 来匹配你想要的内容。 ps:如果不设置这个属性,就会接收全部内容。select 不支持动态变量。

    <!-- 这个是含有ng-content 标签的组件 -->
    <div>
      <div class="content-item">
        这个是没有select属性的<br />
        <ng-content></ng-content>
      </div>
      <div class="content-item">
        这个是匹配标签的<br />
        <ng-content select="h6"></ng-content>
      </div>
      <div class="content-item">
        这个是匹配css的<br />
        <ng-content select=".demo"></ng-content>
      </div>
      <div class="content-item">
        这个是匹配属性的<br />
        <ng-content select="[id=demo]"></ng-content>
      </div>
    </div>
    
    <!-- 这个是调用方 -->
    <demo-ng-content>
      <p>你说的对</p>
      <h6>这个是h6标签</h6>
      <div class="demo">我的class名称是demo</div>
      <div id='demo'>我有id=demo的属性</div>
      结束
    </demo-ng-content>
    

    下面是展示结果,有木有一目了然~ p标签和最后的文字这两块都替换了没有设置select属性的标签; <h6></h6>标签替换了 select="h6"<div class="demo"> 替换了 select=".demo"<div id='demo'> 替换了 select="[id=demo]" 我也是刚了解到这个标签,想到的比较好的应用场景:在样式和布局阶段,可以用这种方式实现,这样只需要维护一个样式,其他调用方就不用关注样式修改了~ 下面是自己写的个简单栗子:

    <!-- 定义表单的公用样式布局 -->
    <main>
      <div class="item">
        <div class="label">
          <ng-content select=".item-label"></ng-content>
        </div>
        <div class="value">
          <ng-content select=".item-value"></ng-content>
        </div>
      </div>
    </main>
    
    <!-- 调用公用布局,填充自己的表单 -->
    <main>
      <div class="body">
        <demo-ng-content>
          <ng-container class="item-label">姓名:</ng-container>
          <input class="item-value" nz-input placeholder="请输入姓名">
        </demo-ng-content>
        <demo-ng-content>
          <ng-container class="item-label">年龄:</ng-container>
          <input class="item-value" nz-input placeholder="请输入年龄">
        </demo-ng-content>
        <demo-ng-content>
          <ng-container class="item-label">身份证:</ng-container>
          <input class="item-value" nz-input placeholder="请输入身份证号码">
        </demo-ng-content>
      </div>
    </main>