Angular 向组件传递模板的几种方法
最近在写一个日期选择器组件,为了满足将来可能出现的各种需求,所以需要能够高度的自定义组件的样式。为了达到这个目的,需要能够在日期选择器组件外控制每个日期格子内要显示的内容,比如,标上节假日之类的。这时候,组件的一部分模板就需要由调用方提供。
在 React 里面,这种需求挺简单的,只要实现一个 date => Element 这样的函数就好了,但是 Angular 模板是纯粹的模板,需要使用一些专门的概念才能实现这个功能。
第一种方式 <ng-content>
<ng-content> 这个标签到本文撰写时为止,还没有官方的文档,甚至连占位符都没有。但是这并不妨碍我们的使用,外国热心网友已经总结出了 <ng-content> 在现阶段的特点与作用。
基本用法
<!-- Wrapper.Component.html -->
<div>
hello
<ng-content></ng-content>
</div>
假设我们有一个上述的组件,然后向下面这样调用:
<wrapper>
<span> World </span>
</wrapper>
那么最终的渲染结果将会是这样的:
<div>
hello
<span> World </span>
</div>
看起来就是发生了很简单的替换,但是如果在 Wrapper 中出现了多个 <ng-content> 会出现多个 <span> World </span> 吗?答案是不会的。<ng-content> 的本质只是移动元素,并不会去自动的创建传入的模板,所以就算用 ngFor 套住 <ng-content> 也不会出现很多个 <span> World </span>。如果传入的是自定义的组件,这些组件也只会被实例化一次。
进阶用法
当然,如果 <ng-content> 的功能仅仅只是这样就显得太鸡肋了,在使用 <ng-content> 的时候可以指定一个选择器,这个选择器可以捕获相符的直接子元素。例如:
<!-- Wrapper.Component.html -->
<div>
hello
<ng-content></ng-content>
<hr/>
<ng-content select="span"></ng-content>
</div>
然后像下面这样使用:
<wrapper>
<span> World </span>
2333
</wrapper>
最终的渲染结果将会是这样:
<div>
hello
2333
<hr/>
<span> World </span>
</div>
除了设置 ng-content 标签的 select 属性之外,还可以在子元素上使用 ngProjectAs 属性,这个属性可以让这个元素被父元素中指定的 ng-content 所捕获。举个例子:
<wrapper>
<div ngProjectAs="span"> World </div>
2333
</wrapper>
这次被传入的模板变成了一个 div,但是因为设置了 ngProjectAs,所以“World”会出现在分割线下方。
第二种方式 NgTemplateOutlet 指令
使用 ng-content 确实可以起到传入模板的效果,但是却有个很致命的问题,就是无法传递数据到传入的模板中。为了将数据传递到传入的模板中,就需要使用到 NgTemplateOutlet 指令。
基本使用
这个指令可以用来在模板的指定位置实例化一个 TemplateRef 对象,同时,在实例化的过程中还可以传入一个数据对象。而 TemplateRef 可以通过 ng-template 标签来创建,举个例子:
@Component({
selector: 'ng-template-outlet-example',
template: `
<ng-container *ngTemplateOutlet="name; context: myContext"></ng-container>
<ng-template #name let-name="data"><span>Hello {{name}}!</span></ng-template>
`
})
class NgTemplateOutletExample {
myContext = {data: 'World'};
}
ng-container 是一个虚拟的元素,在这个元素上我们使用了一个 NgTemplateOutlet 指令,指定了要实例化下面的名为 name 的 ng-template。同时把 myContext 这个对象作为实例化的数据上下文传入,所以最终就会显示 “Hello World!”。值得注意的是在 ng-template 里面获取传输的数据上下文的方式:let-variableName='key'。
进阶使用
接下来就要实现本文开头提到的需求了,在组件外部传入模板。还是以上面的例子为例,因为模板需要由外界作为子内容传入,所以需要我们手动来捕获模板,这里需要就需要使用 ContentChild:
@Component({
selector: 'wrapper',
template: `
<ng-container *ngTemplateOutlet="name; context: myContext"></ng-container>
`
})
class NgTemplateOutletExample {
@ContentChild(TemplateRef) name: TemplateRef<any>;
myContext = {data: 'World'};
}
就是这么简单的改动就可以让我们的组件从外界接受模板了,来试一试:
<wrapper>
<ng-template let-value="data">
<span>Hello {{value}}!</span>
</ng-template>
</wrapper>
总结
以上就是 Angular 中向组件传递模板的两种方法,其中,使用 <ng-content> 标签可以更方便的控制传入的模板在 DOM 中的位置,而 NgTemplateOutlet 可以向传入的模板传递渲染数据,两者搭配使用可以起到很好的效果。
参考:
Angular 向组件传递模板的几种方法的更多相关文章
- 5.MVC框架开发(强类型开发,控制器向界面传递数据的几种方法)
界面表单中的表单元素名字和数据库表的字段名相一一映射(需要哪个表的数据就是那个表的模型(Model)) 在View页面中可以指定页面从属于哪个模型 注:以上的关系可以通过MVC的强类型视图开发来解决我 ...
- react在router中传递数据的2种方法
概述 不传递数据叫什么单页面应用,渲染模块还需要http请求算什么单页面应用. 本文总结了react-router4中使用BrowserRouter时传递数据的两种方法,供以后开发参考,相信对其他人也 ...
- 在React中跨组件分发状态的三种方法
在React中跨组件分发状态的三种方法 当我问自己第一百次时,我正在研究一个典型的CRUD屏幕:"我应该将状态保留在这个组件中还是将其移动到父组件?". 如果需要对子组件的状态进行 ...
- vue-learning:31 - component - 组件间通信的6种方法
vue组件间通信的6种方法 父子组件通信 prop / $emit 嵌套组件 $attrs / $liteners 后代组件通信 provide / inject 组件实例引用 $root / $pa ...
- Vue组件template模板字符串几种写法
在定义Vue组件时,组件的模板template选项需要的是一个字符串,当其内容较复杂需要换行时,需要简单处理一下,具体有五种方式: 方式一:使用 \ 转义换行符 <!DOCTYPE html&g ...
- Java多线程初学者指南(7):向线程传递数据的三种方法
在传统的同步开发模式下,当我们调用一个函数时,通过这个函数的参数将数据传入,并通过这个函数的返回值来返回最终的计算结果.但在多线程的异步开发模式下,数据的传递和返回和同步开发模式有很大的区别.由于线程 ...
- Intent传递对象的两种方法(Serializable,Parcelable) (转)
今天讲一下Android中Intent中如何传递对象,就我目前所知道的有两种方法,一种是Bundle.putSerializable(Key,Object);另一种是Bundle.putParcela ...
- mfc 在VC的两个对话框类中传递参数的三种方法
弄了好久,今天终于把在VC中的对话框类之间传递参数的问题解决了,很开心,记录如下: 1. 我所建立的工程是一个基于MFC对话框的应用程序,一共有三个对话框,第一个对话框为主对话框,所对应的类为CTMD ...
- Intent传递对象的两种方法
Android为intent提供了两种传递对象参数类型的方法 分别需要使实体类实现Serializable接口.Parcelable接口 首先我们要知道,传递对象,需要先将对象序列化 一.那么为什么要 ...
随机推荐
- 算法-java代码实现计数排序
计数排序 第10节 计数排序练习题 对于一个int数组,请编写一个计数排序算法,对数组元素排序. 给定一个int数组A及数组的大小n,请返回排序后的数组. 测试样例: [1,2,3,5,2,3], ...
- asp.net -mvc框架复习(7)-基于MVC搭建用户登录项目框架
整体框架: 一.搭建Model层 1.添加通用数据访问类 2.添加实体类(封装和传递数据,和数据库中数据表对应) 3.添加数据访问类(通常和实体类同名,但是后缀名发生改变) 二.搭建控制器层Contr ...
- 【编程技巧】alert vs Ext.Msg.alert
alert会阻塞程序的运行. Ext.Msg.alert是异步的,它的调用并不会停止浏览器中代码的执行.
- Codeforces 448 D. Multiplication Table 二分
题目链接:D. Multiplication Table 题意: 给出N×M的乘法矩阵要你求在这个惩罚矩阵中第k个小的元素(1 ≤ n, m ≤ 5·10^5; 1 ≤ k ≤ n·m). 题解: n ...
- Mybatis异常:java.lang.ClassNotFoundException: org.mybatis.spring.SqlSessionFactoryBean
问题描述: 一月 15, 2014 3:43:13 下午 org.springframework.context.support.AbstractApplicationContext prepareR ...
- 以守护进程的方式部署flask
1.文件目录 创建一个简单的flask 项目... application = Flask(__name__) application.debug = True 2.安装wsgi pip instal ...
- Java进阶篇(五)——Java的I/O技术
程序中,为了永久的保存创建的数据,需要将其保存在磁盘文件中,以便在其它程序中使用它们.Java的I/O技术可以将数据保存到文本文件.二进制文件甚至是ZIP压缩文件中,以达到永久性保存数据的要求. 本篇 ...
- linux_思想
linux有哪些重要的思想? 1. 做的越多错的越多 2. 纸包不住火 3. 操作重要文件前备份,操作后查看结果 4. 看到命令输出结果,可能命令有个选择直接获得对应值 5. 先定行,再定列
- Newtonsoft.Json 版本冲突时解决方案
如果同一项目中不同第三方类库分别使用了不同版本的Newtonsoft.Json的情况下,可以在主项目配置文件中添加以下节点,将0.0.0.0-11.0.0.0此区间的Newtonsoft.Json使用 ...
- 反编译class文件并重新编译的方法
在没有.java源码的情况下,如果想修改一个.class文件.可以通过以下步骤实现: 修改前的class文件: 一.反编译.class文件成.java文件. 1.可以使用Java Decompiler ...