本章节将要介绍如何使用 ng-content 将一个 Component 的内容投影到另一个 Component 中,创建灵活且可被重复使用的 Component,来满足程序设计中 DRY (Don't repeat yourself) 的观念。
在 Angular 中 Content 是一种呈现的模式,可以在其中插入或投影另一个 Component 的内容,简单来说,当我们在写 HTML 时常常会有这样的结构:
<div>
<p> Hello World!</p>
</div>
在 Angular 的概念中可以把它想像成,有一个 <p>
tag 被投影到 <div>
之中,虽然不是原理可能不这样子,但是类似的模式。
在 Angular 中有几种常见的 ng-content 例子:
在 Angular 的 ng-content 中最基本的就是 single-slot content projection,他是指将一个 Component 的内容投影在另一个 Component 之中
,我们做一个简单的小例子来解释这个行为。
首先我们先使用 Angular CLI 建立一个 Component
ng generate component zippy-basic
ng g c zippy-basic
在 zippy-basic.component.html 中添加 ng-content 到你希望投影内容出现的位置
<!-- zippy basic.component.html -->
<h2>Single-slot content projection</h2>
<div>
ng-content content:
<ng-content></ng-content>
</div>
接着我们在 app.component.html 中使用 zippy-basic 的 selector
<!-- app.component.html -->
<app-zippy-basic></app-zippy-basic>
在画面中我们看到,我们使用 ng-content 的位置什麽都没有,是出了什麽 bug 吗?其实不是,是因为我们还没决定该把什麽内容投影在 的位置上,我们来更改一下 app.component.html 的内容
<!-- app.component.html -->
<app-zippy-basic>
<p>From app.component.html projection content to zippy-basic component</p>
</app-zippy-basic>
当更改玩 app.component.html 的内容後,可以在画面中看到,Angular 把我们夹在 <app-zippy-basic>
的内容放到 <ng-content>
的位置了,
当然可能有人会问了,那...我直接把 <p>
放在 zippy-basic.component.html 不就好了吗?
<!-- zippy basic.component.html -->
<h2>Single-slot content projection</h2>
<div>
ng-content content:
<p>From app.component.html projection content to zippy-basic component</p>
</div>
没有错,但是其实 ng-content 通常不是让你放 html tag 的,我们来举个例子,想像一下一个场景,当你在开发专案时,有没有遇到在一个地方需要放置三种不同类型的 button ? 如果是这样的话你是不是会这麽做
<div>
<!-- style1 button -->
<label>This is style 1 button</label>
<p>Hello world</p>
<button>button</button>
<div>
<div>
<!-- style2 button -->
<label> This is style 2 button </label>
<p>Hello world</p>
<button>button</button>
<div>
<div>
<!-- style3 button -->
<label> This is style 3 button </label>
<p>Hello world</p>
<button>button</button>
<div>
有没有发现当使用上面这种 HTML 结构虽然可以达到我们要的目的,但是 <label>
、<p>
、<button>
一直在重复出现但内容却是一样的,就违背了 DRY 的原则了,这时候我们就可以使用 ng-content 先将模板做好,再把不同 style 的 投影进去就好,来举个例子吧:
首先我们先建立一个模板 Component
ng generate component content-template
ng g c content-template
接着我们在 content-template.component.html 中把我们的模板写好
<!-- content-template.component.html -->
<h1>Content projection number {{styleCount}}</h1>
<label>This is style {{styleCount}} button</label>
<p>Hello world</p>
<ng-content></ng-content>
我们在原本放置不同 style 的地方使用 变成将别的内容投影到这个位置上,对了还记得昨天提到的 @Input( ) 吗? 这边我们来复习一下,使用 ng-content 一样可以由父层传递数据到子层喔
。
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-content-template',
templateUrl: './content-template.component.html',
})
export class ContentTemplateComponent {
@Input() styleCount: string = '';
constructor() { }
}
接着我们来更改一下 app.component.html 的内容,让不同 style 的 button 投影到 content-template 吧
<!-- app.component.html -->
<app-content-template [styleCount]="'1'">
<button class="style1">Click Me</button>
</app-content-template>
<app-content-template [styleCount]="'2'">
<button class="style2">Click Me</button>
</app-content-template>
<app-content-template [styleCount]="'3'">
<button class="style3">Click Me</button>
</app-content-template>
这样就完成了我们的目的,简单的几行就可以达到相同的目的,透过先建立模板再将不同样式或不同的 Component 投影进这个模板 Component 之中
,就可以让这个模板 Component 在各个地方都被使用并且达到减少重复程序码的目的。
第二种其实与第一种非常相似,只不过变成了一个 Component 中投影了多个不同的内容,不过值得注意的是,由於是多个不同的内容投影在一个 Component 之中,所以就会有顺序位置问题,於是 Angular 提供了 select
属性让你加在 ng-content 上,让你可以将某一个内容放在指定的位置上
,一样举个例子吧。
首先一样创建一个新的 Component
ng generate component zippy-basic
ng g c zippy-basic
接着我们在 zippy-basic.component.html 中添加 ng-content 到想要的位置
<!-- zippy-basic.component.html -->
<h2>Multi-slot content projection</h2>
Default:
<ng-content></ng-content>
Question:
<ng-content></ng-content>
接着我们把想要的位置添加一个 select 属性,让投影的位置固定在我们想要的地方
<!-- zippy-basic.component.html -->
<h2>Multi-slot content projection</h2>
Default:
<ng-content></ng-content>
Question:
<ng-content select="[question]"></ng-content>
最後我们在 app.component.html 中将我们像要投影的内容放进去,记得!指定位置的内容需要加上 select 的内容喔
<!-- app.component.html -->
<app-zippy-basic>
<p question>Is content projection cool?</p>
<p>Let's learn about content projection!</p>
</app-zippy-basic>
可以看到我们在 app.component.html 中 的顺序与 UI 呈现的顺序是颠倒的,就因为我们指定了有 question 这个 select 放置的地方。
与 Single-slot content projection 一样,Multi-slot content projection 的目的不在於放入原生 HTML 的 Tag,目的与上一个一样,都是可以先建立一个模板之後将不同的内容投影到这个模板中,只不过多了可以透过 select 让你选择放置的位置
与让你可以放多个投影
,这边就不在做一次练习了,有兴趣的话可以自己挑战一下利用 Multi-slot content projection 完成与上面的例子一样的内容。
在本章中介绍了什麽是 content projection 以及该如何使用它,在官方文档中其实除了 Single-slot content projection 与 Multi-slot content projection 之外其实还有第三个常见的投影 Conditional content projection
,但是因为他牵扯的概念跟本篇章使用 ng-content 比较不一样,他是透过使用 ng-template
来做到这个功能,所以就先不将它纳入本章的范围,这个方法会在之後介绍到。
下一篇将会分享 template 是什麽,他在 Angular 中是 HTML 的角色,而可以在 template 中使用许多语法来达到灵活建立画面的功能,那我们就下一篇再见吧。
<<: 【Day7】Vocoder Model 以及 WaveNet 介绍
相信网路上其实已经有不少文章在谈架构了,我的资历也尚浅,今天虽然会介绍架构,但是主要会侧重的点会是...
开发Android需要经历些什麽样的心酸?...不是说iOS开发不会有心酸事,一想到「天线门」、「T...
1.grid的justify-content水平对齐、align-content垂直对齐 =>...
建构元 在JAVA中,建构元所扮演的主要角色,是帮助新建立的物件设定初值。 修饰子 类别名称(型态1...
安安,今天继续讲伪类选择器欧!看书才发现...omg原来还有这麽多不知道的伪类选择器!!!原本以为一...