https://www.jianshu.com/p/0f5332f2bbf8

ng-template、ng-content、ng-container三者应该是自定义组件需要经常用到的指令。今天咱们就来简单了解下ng-template、ng-content、ng-container的基本用法。

一、ng-content

ng-content是内容映射指令(也叫内容嵌入),内容映射指的是在组件中嵌入模板代码,方便定制可复用的组件,很好地扩充组件的功能,方便代码的复用。

我们再往简单一点想ng-content相当于一个占位符(留了个位置),类似于路由出口router-outlet一样。之后会把相应的内容放到这个位置上来。

1.2 ng-content select 组件选择

ng-content提供select 属性,方便我们选择投影的内容(组件或者html里面的标签),如果我们没有设置select属性则所有的内容都可以投影上来。select的选择规则很简单,就三种规则:

  • select="xx"选择,xx对应html里面标签或者组件的名字。比如select="div"表示ng-content位置只会放div标签。
  1. <ng-content select="div"></ng-content>
  • select=".xx"选择,xx对应html标签或者组件的class名字。比如select=".select-class"表示ng-content位置只会放设有class="select-class"的html标签或者组件。
  1. <ng-content select=".select-class"></ng-content>
  • select="[key=value]"选择,key-value的形式。选择设置了属性key="value“的html标签或者组件。比如select="[name=test]"表示ng-content位置只会放设置了属性name=”test“的html标签或者组件。

select="[key]" 也是类型,ng-content会选择设置有key的属性的html标签或者组件。

  1. <ng-content select="[name=test]"></ng-content>
  2. <div name="test">我是第一号位置 div[name="test"]</div>

强调一点select的值不能设置为动态的

1.2 ngProjectAs

通过ng-content的select属性可以指定html标签或者组件投射ng-content位置上来。但是呢有个限制条件。不管是select标签或者组件的名字、或者class、或者是属性他们都是作用在直接子节点上。还是用一个简单但额实例来说明

  1. // 我们先自定义一个组件app-content-section 里面会使用ng-content
  2. import {Component} from '@angular/core';
  3. @Component({
  4. selector: 'app-content-section',
  5. template: `
  6. <div>
  7. <h1>ng content</h1>
  8. <div style="background-color: #039be5">
  9. <ng-content select="app-content-child"></ng-content>
  10. </div>
  11. </div>
  12. `,
  13. styleUrls: ['./content-section.component.less']
  14. })
  15. export class ContentSectionComponent {
  16. }
  17. // 下面中情况下 ng-content没有投射到对应的内容
  18. <app-content-section>
  19. <ng-container>
  20. <app-content-child [title]="'测试下'"></app-content-child>
  21. </ng-container>
  22. </app-content-section>
  23. // 通过使用 ngProjectAs 让ng-content的内容能正确的投射过来。
  24. <app-content-section>
  25. <ng-container ngProjectAs="app-content-child">
  26. <app-content-child [title]="'测试下'"></app-content-child>
  27. </ng-container>
  28. </app-content-section>

1.3 ng-conent 包含组件的获取

ng-conent提供了@ContentChild和@ContentChildren来获取ng-conent里面包含的组件(类似@ViewChild和@ViewChildren)。获取到ng-conent里面的组件之后你就可以为所欲为了。

我觉得如果有需要获取ng-content包含组件的情况,前提条件是咱得对放在ng-content位置的是啥类型的组件心里有数。有一点要注意@ContentChild和@ContentChildren所在的ts文件一定是有ng-content对应的哪个ts文件。可千万别搞错了。

接下来我们用一个特别简单的例子

  1. // 定义一个app-content-section组件
  2. import {AfterContentInit, Component, ContentChild, ContentChildren, QueryList} from '@angular/core';
  3. import {ContentChildComponent} from '../child/content-child.component';
  4. /**
  5. * 想获取ng-content里面的组件的使用@ContentChild或者@ContentChildren
  6. */
  7. @Component({
  8. selector: 'app-content-section',
  9. template: `
  10. <div>
  11. <h1>ng content</h1>
  12. <!--这里我们确定我们这里会放ContentChildComponent组件,才好使用@ContentChild和@ContentChildren-->
  13. <ng-content></ng-content>
  14. </div>
  15. `,
  16. styleUrls: ['./content-section.component.less']
  17. })
  18. export class ContentSectionComponent implements AfterContentInit {
  19. // 通过 #section_child_0 获取组件
  20. @ContentChild('section_child_0')
  21. childOne: ContentChildComponent;
  22. // 通过 ContentChildComponent 组件名获取组件
  23. @ContentChildren(ContentChildComponent)
  24. childrenList: QueryList<ContentChildComponent>;
  25. ngAfterContentInit(): void {
  26. console.log(this.childOne);
  27. this.childrenList.forEach((item) => {
  28. console.log(item);
  29. });
  30. }
  31. }
  32. // 使用app-content-section
  33. import {Component} from '@angular/core';
  34. @Component({
  35. selector: 'app-ng-content',
  36. template: `
  37. <app-content-section>
  38. <app-content-child #section_child_0 [title]="title_0"></app-content-child>
  39. <app-content-child #section_child_1 [title]="title_1"></app-content-child>
  40. </app-content-section>
  41. `,
  42. styleUrls: ['./ng-content.component.less']
  43. })
  44. export class NgContentComponent {
  45. title_0 = 'child_0';
  46. title_1 = 'child_1';
  47. }

二、ng-template

ng-template是Angular 结构型指令中的一种,用于定义模板渲染HTML(模板加载)。定义的模板不会直接显示出来,需要通过其他结构型指令(如 ng-if)或 template-ref 将模块内容渲染到页面中。

上面既然说了ng-template的内容默认是不会在页面上显示出来的。这肯定不行呀,咱们使用ng-template不管中间的原因是啥,反正最后肯定是要把这些内容显示在界面上的。那么咱们有哪些办法来显示ng-template的内容呢。

  • 借助其他结构型指令如×ngIf,来显示ng-template的内容。
  1. <!-- 通过ngIf结构型指令显示ng-template的内容 -->
  2. <div class="lessons-list" *ngIf="condition else elseTemplate">
  3. 判断条件为真
  4. </div>
  5. <ng-template #elseTemplate>
  6. <div>判断条件为假</div>
  7. </ng-template>
  • 通过TemplateRef、ViewContainerRef把ng-template对应的元素显示出来。TemplateRef对应ng-template的引用,ViewContainerRef呢则是view容器的引用用来操作DOM元素。
  1. import {AfterViewInit, Component, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core';
  2. @Component({
  3. selector: 'app-template-section',
  4. template: `
  5. <ng-template #tpl>
  6. Hello, ng-template!
  7. </ng-template>
  8. `,
  9. styleUrls: ['./template-section.component.less']
  10. })
  11. export class TemplateSectionComponent implements AfterViewInit {
  12. @ViewChild('tpl')
  13. tplRef: TemplateRef<any>;
  14. constructor(private vcRef: ViewContainerRef) {
  15. }
  16. ngAfterViewInit() {
  17. // 这样tplRef对应的ng-template内容就显示出来了
  18. this.vcRef.createEmbeddedView(this.tplRef);
  19. }
  20. }
  • 通过NgTemplateOutlet指令来显示已有的ng-template对应的视图。NgTemplateOutlet指令用于基于已有的 TemplateRef 对象,插入对应的内嵌视图。同时我们可以通过 [ngTemplateOutletContext] 属性来设置 ng-template 的上下文对象。绑定的上下文应该是一个对象,ng-template中通过 let 语法来声明绑定上下文对象属性名。
  1. import {AfterViewInit, Component, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core';
  2. @Component({
  3. selector: 'app-ng-template',
  4. template: `
  5. <!-- let-param 取的是绑定对象myContext的$implicit字段的指,let-param相和let-param="$implicit"是等价的, -->
  6. <!-- let-name="name" 取的是绑定对象myContext里面name字段对应的值-->
  7. <ng-template #inputTemplateWithContent let-param let-name="name">
  8. <div>{{param}} - {{name}}</div>
  9. </ng-template>
  10. <ng-container *ngTemplateOutlet="inputTemplateWithContent; context: myContext"></ng-container>
  11. `,
  12. styleUrls: ['./ng-template.component.less']
  13. })
  14. export class NgTemplateComponent {
  15. myContext = {$implicit: '默认值', name: 'tuacy'};
  16. }

这里想稍微提下通过NgTemplateOutlet绑定上下文对象的问题,在ng-template中我们通过let-xx="yy" xx是在ng-template内部使用的变量名字。xx对应的值是上下文对象yy属性的值。而且let-xx等同于let-xx="implicit: '默认值', name: 'tuacy'};
ng-template里面let-param param对应的是myContext对象里面$implicit属性的值(let-param和let-param=”implicit“是一样的意思),let-name="name" name对应的是myContext对象里面name属性对应的值。

我们来一个稍微复杂一点的例子,我们把ng-template的内容,做为一个组件的参数。

  1. import {Component, Input, TemplateRef} from '@angular/core';
  2. @Component({
  3. selector: 'app-template-input',
  4. template: `
  5. <!-- 没有传递参数的时候就使用defaultTemplate里面的布局 -->
  6. <ng-template #defaultTemplate>
  7. <div>咱们没有传递参数</div>
  8. </ng-template>
  9. <ng-container *ngTemplateOutlet="inputTemplate ? inputTemplate: defaultTemplate"></ng-container>
  10. `,
  11. styleUrls: ['./template-input.component.less']
  12. })
  13. export class TemplateInputComponent {
  14. /**
  15. * 模板作为参数
  16. */
  17. @Input()
  18. inputTemplate: TemplateRef<any>;
  19. }
  20. // 使用的时候的代码
  21. <!-- 我们定义一个组件,把ng-template的内容作为参数传递进去 -->
  22. <ng-template #paramTemplate>
  23. <div>我是参数</div>
  24. </ng-template>
  25. <app-template-input [inputTemplate]="paramTemplate"></app-template-input>

2.1 ngTemplateOutlet

ngTemplateOutlet指令用于基于已有的 TemplateRef对象,插入对应的内嵌视图。同时在使用 ngTemplateOutlet 指令的时候,我们可以通过 [ngTemplateOutletContext]属性来设置 EmbeddedViewRef 的上下文对象。绑定的上下文应该是一个对象,而且可以通过 let 语法来声明绑定上下文对象属性名。

在渲染页面之前,Angular 会把及其内容替换为注释


三、ng-container

ng-container既不是一个Component,也不是一个Directive,只是单纯的一个特殊tag。ng-container可以直接包裹任何元素,包括文本,但本身不会生成元素标签,也不会影响页面样式和布局。包裹的内容,如果不通过其他指令控制,会直接渲染到页面中。

咱们可以把ng-container简单理解为一个逻辑容器。用来做一些逻辑处理的。

咱们在讲ng-template的时候就出现了ng-container。ng-container一个重要的作用就是和ng-template一起使用。ng-container还有一个用处就是配合ngFor和×ngIf使用。我们知道ngFor和×ngIf不能同时处在一个元素上。所以咱们想要在不添加额外的html标签的情况下达到同样的效果就得请出ng-container。具体参见如下的代码。

  1. import {Component} from '@angular/core';
  2. @Component({
  3. selector: 'app-ng-container',
  4. template: `
  5. <h1>ng-container</h1>
  6. <ul>
  7. <ng-container *ngFor="let item of list;let index=index">
  8. <li *ngIf="index%2 === 0">
  9. {{"index is " + index + " value is " + item}}
  10. </li>
  11. </ng-container>
  12. </ul>
  13. `,
  14. styleUrls: ['./ng-container.component.less']
  15. })
  16. export class NgContainerComponent {
  17. list = ['1号位置', '2号位置', '3号位置', '4号位置', '5号位置', '6号位置', '7号位置', '8号位置', '9号位置'];
  18. }

关于ng-template、ng-content、ng-container的内容咱们就讲这些。也是为了咱们解析来的自定义控件做一些准备。 本文中设计到的代码下载地址 https://github.com/tuacy/ng-dynamic

作者:tuacy
链接:https://www.jianshu.com/p/0f5332f2bbf8
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

ng-template、ng-content、ng-container的更多相关文章

  1. SVNKit学习——基于Repository的操作之print repository tree、file content、repository history(四)

    此篇文章同样是参考SVNKit在wiki的官方文档做的demo,每个类都可以单独运行.具体的细节都写到注释里了~ 开发背景: SVNKit版本:1.7.14 附上官网下载链接:https://www. ...

  2. Django基础--Django基本命令、路由配置系统(URLconf)、编写视图、Template、数据库与ORM

    web框架 框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构. 使用框架可以帮你快速开发特定的系统. 简单地说,就是你用别人搭建好的舞台来做表演. 尝试搭建一个简单 ...

  3. Android学习笔记——文件路径(/mnt/sdcard/...)、Uri(content://media/external/...)学习

    一.URI 通用资源标志符(Universal Resource Identifier, 简称"URI"). Uri代表要操作的数据,Android上可用的每种资源 - 图像.视频 ...

  4. 详细分析Orchard的Content、Drivers, Shapes and Placement 类型

    本文原文来自:http://skywalkersoftwaredevelopment.net/blog/a-closer-look-at-content-types-drivers-shapes-an ...

  5. 一天搞定CSS:盒模型content、padding、border、margin--06

    1.盒模型 网页设计中常听的属性名:内容(content).填充(padding).边框(border).边界(margin), CSS盒子模式都具备这些属性. 这些属性我们可以用日常生活中的常见事物 ...

  6. 6、Android Content Provider测试

    如果你的应用中使用了Content Provider来与其他应用进行数据交互,你需要对Content Provider进行测试来确保正常工作. 创建Content Provider整合测试 在Andr ...

  7. IDEA04 工具窗口管理、各种跳转、高效定位、行操作、列操作、live template、postfix、alt enter、重构、git使用

    1 工具窗口管理 所有的窗口都是在view -> tools windows 下面的,这些窗口可以放在IDEA的上下左右各个位置:右键某个窗口后选择move to 即可进行位置调整 2 跳转 2 ...

  8. Android 文件路径(/mnt/sdcard/...)、Uri(content://media/external/...)

    一.URI 通用资源标志符(Universal Resource Identifier, 简称"URI"). Uri代表要操作的数据,Android上可用的每种资源 - 图像.视频 ...

  9. Django Template语法中 OneToOne、ForeignKey 外键查询

    主表的Models的结构 class A(models.Model): username = models.CharField(max_length=32, verbose_name='用户名称') ...

  10. e3mall商城的归纳总结5之修改商品分类、e3mall—content的搭建

    说在前面的话 本节基本上没有用到新的知识点.主要还是对数据库的增删改查以及创建了一个新的内容模块. 新增商品分类 由于easyUI的Tree需要三个字段(Id.state.text), [{ &quo ...

随机推荐

  1. Linux安装MySql5.7及配置(yum安装)

    Linux安装MySql5.7及配置(yum安装) [root@xld ~]# rpm -q centos-release centos-release-7-7.1908.0.el7.centos.x ...

  2. FLUME安装&环境(二):拉取MySQL数据库数据到Kafka

    Flume安装成功,环境变量配置成功后,开始进行agent配置文件设置. 1.agent配置文件(mysql+flume+Kafka) #利用Flume将MySQL表数据准实时抽取到Kafka a1. ...

  3. linux系统高级命令进阶

    输出重定向 >:覆盖文件内容 echo "123" > test:把原来的内容覆盖 echo "123" >> test:把原来的存在( ...

  4. 应用安全 - 平台 | 工具 - Nexus漏洞汇总

    CVE-2019-5475 Date: 类型: RCE 影响范围: Nexus Repository Manager OSS <= Nexus Repository Manager Pro &l ...

  5. 【Qt开发】【VS开发】VS2010+Qt开发环境搭建

    QT与JAVA有点类似,也是一种跨平台的软件(当然在windows平台和linux平台需要安装相应的QT开发环境和运行库,类似于JAVA在不同平台下的虚拟机JVM环境),因此对于某些需要同时支持win ...

  6. vue+sentry 前端异常日志监控

    敲代码最糟心不过遇到自己和测试的环境都OK, 客户使用有各种各样还复现不了的问题,被逼无奈只能走到这一步:前端异常日志监控! vue官方文档如下推荐: 就是说, vue有错误机制处理errorHand ...

  7. RBAC----基于角色的访问权限控制

    RBAC是什么? 基于角色的权限访问控制(Role-Based Access Control) 作为传统访问控制(自主访问.强制访问)的有前景的代替 受到了广泛的关注. 在RBAC中,权限与角色相关联 ...

  8. 6.float类型 和 char 类型

    float32  float64 package main import "fmt" func main() { var xxx float32 var xxxx float64 ...

  9. Java关于继承中的内存分配

    1.定义         super:当前对象的父类对象         this   :当前对象,谁调用this所在的方法,this就是哪一个对象.   2.内存分析 另一个例子: public s ...

  10. unsolved question's solution

    因为很懒,没有时间,只会口胡等等原因,所以有些题目就不打code了 $luogu:$ P1973 [NOI2011]Noi嘉年华: 时间离散化,预处理一个区间$[l,r]$内的最多活动个数$in[l] ...