# 内容投影

angular 有这么3个元素标签 ng-container ng-content ng-template , 开发经常用到,官网对他们的介绍却少之又少。

# ng-container

等同 微信小程序的<block>, vue 的<template> , 作为包装元素,不会在dom中产生标签,可以接收控制属性。

<template> 是html5原生标签,理论上 angular 也可以用, 但是被angular官方优化掉了并且和ng-template不是一回事。

如下: 我们在做复杂的条件循环的时候,经常用到 ng-container 来执行 *ngFor*ngIf的同时又不会产生额外的标签。

<!-- 侧边栏部分代码 -->
 <ul nz-menu nzMode="inline" class="sider-menu scrollbar" >
   <ng-container *ngFor="let item of menuList">
       <li nz-submenu *ngIf="!item.nzIconfont" [nzTitle]="item.name" [nzIcon]="item.ico">
         <ul>
           <li *ngFor="let it of item.children" [routerLink]="it.link" nz-menu-item >
            {{it.name}}
           </li>
         </ul>
       </li>
   </ng-container>
 </ul>

# ng-content

等同于 vue的 slot插槽。 官方的解释是:内容投影是一种模式,你可以在其中插入或投影要在另一个组件中使用的内容。比如,你可能有一个子组件,它可以接受父级插入的内容。

TIP

<ng-content> 元素是一个占位符,它不会创建真正的 DOM 元素。<ng-content> 的那些自定义属性将被忽略。

# 单插槽

假设应用场景是这样的,有一个组件展示班级个人信息。

<!-- 使用组件 -->
<app-detail-info></app-detail-info>
<!-- detail-info-component.html -->
<p>小明-1995年-男</p>

现在要统一加上学校信息,这时候可以利用内容投影

<!-- 使用组件 -->
<app-detail-info>
  光明小学
</app-detail-info>
<!-- detail-info-component.html -->
<ng-content></ng-content>
<p>小明-1995年-男</p>
<!-- 结果 -->
 光明小学
 小明-1995年-男

# 多插槽

如果需要插入多段,我们也可以在对应的<ng-content>标签加上select

<!-- 使用组件 -->
<app-detail-info>
  <div grade>一年级</div>
  <div class="school">光明小学</div>
  <div ngProjectAs="[cls]">二班</div>
  中国
</app-detail-info>
<!-- detail-info-component.html -->
<ng-content></ng-content>
<ng-content select=".school"></ng-content>
<ng-content select="[grade]"></ng-content>
<ng-content select="[cls]"></ng-content>
<ng-content></ng-content>
<p>小明-1995年-男</p>
<!-- 结果 -->
 中国
 光明小学
 一年级
 一班
 小明-1995年-男

TIP

如果你的组件包含不带 select 属性的 <ng-content> 元素,则该实例将接收所有与其他 <ng-content> 元素都不匹配的投影组件。

在示例中,第一个 <ng-content> 就接收投影到组件中的匹配不到的其他内容。

# ng-template

<ng-template>元素定义了一个默认不渲染的模板。常规用法如下

# 1. 当作 NgIf else的子句

<div *NgIf="boolean" else elseBlock ></div>
<ng-template #elseBlock>...</ng-template>

其实 *NgIf 指令是一种简写

<div *ngIf="hero" class="name">{{hero.name}}</div>

<!-- *ngIf变成属性型指令,并绑定在ng-template元素上; -->
<!-- ngIf的宿主元素被包含在ng-template元素内 -->
<ng-template [ngIf]="hero">
  <div class="name">{{hero.name}}</div>
</ng-template>

# 2.插入由模板创建的视图

<ng-container *ngTemplateOutlet="tpl; context: myContext"></ng-container>
<ng-template #tpl let-nickname="name">
    <span>I am {{nickname}}</span>
</ng-template>
myContext = {$implicit: 'bob', name: 'jone'};

TIP

ng-template 在 组件封装 和 结构指令封装 中非常常见,后续会在单独章节中说明。