Angular官方开发文档讲的很细致,但是这是一把双刃剑。
双刃剑
上一篇中Angular CLI
的文档,并非Angular官方开发文档的一部分。(可能我没发现吧)
而从这篇文章开始,我将开始阅读Angular的官方开发文档
。
首先,我阅读完了教程
、核心知识-构架
,粗略的了解下Angular
的开发流程和一些基本的概念。
然后,就是看核心知识-组件与模板
了,看完了第一遍其实,还是有点懵。
有几点感受:
- 可能是因为是英译,有些地方感觉翻译的有点不够接地气,不太便于国人理解
- 文字过于冗长,有时候让人很难抓到重点
- 像看小说,一定要循序渐进慢慢看,不然稍微跳一下,剧情就接不上了
- 看过的vue文档的朋友,一定会感觉vue的文档简洁而清晰
所以在看Angular文档的同时,提炼总结下,还是很有必要的。
1.Attribute & Property
官方文档中强调: 定模板绑是通过 property 和事件来工作的,而不是 attribute。
在Angular开发中,个人感觉一定要明确这两者的别
,才能更好的理解Angular的工作方式。不然很容易产生混淆
,因此我把这一小节放到最前面。
attribute
是指HTML attribute
property
是指DOM property
Angular世界中所说的【属性】,基本上应该是指【property】
attribute
在 Angular 中 唯一的作用是用来初始化元素和指令的状态
。 当进行数据绑定时,只是在与元素和指令的 property
和事件打交道,而 attribute 就完全靠边站了。
2.插值
2.1.文本
<p>My current hero is {{currentHero.name}}</p>
2.2.原始Html
Angular 数据绑定对危险 HTML 有防备。 在显示它们之前,它会对内容先进行处理
。
不管是插值表达式还是属性绑定,都不会允许带有 script 标签的 HTML 泄漏到浏览器中。
src/app/app.component.ts
htmlWithEvil = '<h1>be careful</h1><br><script>alert("evil never sleeps")</script>';
src/app/app.component.html
<!-- 方式1 -->
<h1>{{htmlWithEvil}}</h1>
<!-- 方式2 -->
<h1 [innerHTML]="htmlWithEvil"></h1>
方式1:直接插值
,显然Angular不会这么轻易让你直接插html进来。htmlWithEvil会被转义后输出,html不会有任何效果
方式2:绑定innerHTML
属性,htmlWithEvil中的<script>
被过滤后,html生效
2.3.模板表达式
指的是{{expression}} 中的 expression
方便起见本文中的expression
均是指模板表达式
expression
不仅可以在花括号里面用,可以在属性绑定
的时候用
expression
的语法和JavaScript相似
,但不完全相同
与JavaScript的区别expression
不予许出现:
- 赋值 (=, +=, -=, ...)
- new 运算符
- 使用 ; 或 , 的链式表达式
- 自增和自减运算符:++ 和 --
- 不支持位运算 | 和 &
2.4.模板表达式运算符
模板表达式运算符
是JavaScript中没有的
的特性
2.4.1.管道操作符
管道操作符|
,可对表达式结果进行一些转换
<div>Title through uppercase pipe: {{title | uppercase}}</div>
想了解更多Angular自带的管道,可以官网查管道的API
2.4.2.安全导航操作符和空属性路径
可以完美的解决空值异常,增加视图的容错性。
{{order?.consignee?.mobile}}
即使consignee
是个空值
,也不会报异常,只是不显示而已
。可以完美的解决空值异常,增加视图的容错性。
2.4.3.非空断言操作符
如果你打开了严格检测
,那就要用到这个模板操作符,而其它情况下则是可选的
。
<!--No hero, no text -->
<div *ngIf="hero">
The hero's name is {{hero!.name}}
</div>
在 TypeScript 2.0 中,你可以使用 --strictNullChecks
标志强制开启严格空值检查。TypeScript 就会确保不存在意料之外的 null 或 undefined。
3.属性绑定
3.1.基本语法
[property]="expression"
3.1.三种写法
<!-- 例1 -->
<img [src]="heroImageUrl">
<!-- 例2 -->
<img bind-src="heroImageUrl">
<!-- 例3 -->
<img src="{{heroImageUrl}}">
- 以上三种方式,效果是相同的
-
例1
、例2
,都是属于纯正的属性绑定
语法 -
例3
,血统不纯正,不推荐
使用
估计是为了兼顾AngularJs
的使用习惯
虽然在渲染视图之前,Angular会把例3
翻译成相应的属性绑定
但当property
数据类型不是字符串
时,会产生问题
。后面3.2举例
上面这个例子中,img
的attribute
src与image
的property
src相同,所有感觉让人产生混淆
3.2.容易混淆之处
<!-- 例1 -->
<span [innerHTML]="htmlWithEvil"></span>
<!-- 例2 -->
<span innerHTML="{{htmlWithEvil}}"></span>
两者效果完全相同
,再次印证:方括号里面的其实是property
,而不是,不是,不是attribute
。
<button [disabled]="isDisabled">Save</button>
<button bind-disabled="isDisabled">Save</button>
<button disabled="{{isDisabled}}">Save</button>
- 如果当
isDisabled
值为true
的时候,三个按钮都禁用了,没毛病。 - 如果当
isDisabled
值为false
的时候,前两个按钮可用了,没毛病。但是!第三个按钮依然还是禁用
。
应该是把false
转成'false'
后,赋值给了disabled
这个property
所以,不要以为你用TypeScript
在写Angular
,就可以忘了JavaScript
这个弱类型语言。
最终建议:为了避免不必要的麻烦,还是使用标准语法
为好.
3.3.组件属性
属性绑定,同样适用于自定义组件
。不过需要注意的是,在绑定属性时,务必传入匹配的数据类型
<app-hero-detail [hero]="currentHero"></app-hero-detail>
app-hero-detail
组件的 hero 属性想要一个 Hero 对象,那就在属性绑定中精确地给它一个 Hero 对象
4.attribute绑定
基本语法:[attr.colspan]="expression"
<table>
<tr>
<td>1</td>
<td>2</td>
</tr>
<tr>
<td [attr.colspan]="1 + 1">One-Two</td>
</tr>
</table>
当有些元素的attribute
并没有对应property
时,就可以这么干
考虑 ARIA, SVG 和 table 中的 colspan/rowspan 等 attribute。 它们是纯粹的 attribute,没有对应的属性可供绑定。
5.class绑定
5.1.基本语法
[class.class-name]="isSpecial"
,isSpecial值为 true
/false
追加
<div class="a-class b-class" [class.c-class]='condition'>xxx</div>
condition = true
时,会在原有class基础上,追加c-class。实际输出:
<div class="a-class b-class c-class">xxx</div>
覆盖
<div class="a-class b-class" [class]='classTxt'>xxx</div>
classTxt = 'c-class'
时,将会覆盖原有class。实际输出:
<div class="c-class">xxx</div>
这种做法,似乎很鸡肋
,没想出使用场景。我就是列出来,做个区分
痛点
<div class="a-class b-class"
[class.c-class]='cCondition'
[class.d-class]='dCondition'
...
>xxx</div>
如果有N个class,需要用变量作为条件控制,这就变的很尴尬。
这个时候就要祭出NgClass
了。
5.2.NgClass
用法示例:
<!-- 字符串 -->
<div class="a-class" [ngClass]="'c-class d-class'">xxx</div>
<!-- 数组 -->
<div class="a-class" [ngClass]="['c-class','d-class']">xxx</div>
<!-- 对象:开关单个class -->
<div class="a-class" [ngClass]="{'c-class':condtionC,'d-class':condtionD}">xxx</div>
<!-- 对象:开关一组class -->
<div class="a-class" [ngClass]="{'c-class d-class':condtionCD}">xxx</div>
a-class
始终存在,[ngClass]
控制是否追加c-class
、d-class
可见NgClass,非常灵活
想不明白,为啥还要提供[class]这种操作方式。像vue就只是提供了:class
而已。
有时候选择多了,也未必是件好事儿。
6.style绑定
6.1.基本命令
[style.style-property(.unit)]="value"
<button [style.color]="isSpecial ? 'red': 'green'">Red</button>
<button [style.background-color]="canSave ? 'cyan': 'grey'" >Save</button>
<button [style.font-size.em]="isSpecial ? 3 : 1" >Big</button>
<button [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button>
不多说,提炼备忘
6.2.NgStyle
<div style="background: red;" [ngStyle]="{'color': 'white'}">xxx</div>
<div style="color: yellow;" [ngStyle]="{'color': hasGreenHat?'green':'red'}">xxx</div>
<div style="background: yellow;" [ngStyle]="varInComponent">xxx</div>
- 可在原有样式基础上
追加
- 可
覆盖
原有的某个
样式 - 可直接赋值
上下文变量
7.条件渲染
7.1.NgIf
<app-hero-detail *ngIf="isActive"></app-hero-detail>
7.2.NgSwitch
<div [ngSwitch]="currentHero.emotion">
<app-happy-hero *ngSwitchCase="'happy'" [hero]="currentHero"></app-happy-hero>
<app-sad-hero *ngSwitchCase="'sad'" [hero]="currentHero"></app-sad-hero>
<app-confused-hero *ngSwitchCase="'confused'" [hero]="currentHero"></app-confused-hero>
<app-unknown-hero *ngSwitchDefault [hero]="currentHero"></app-unknown-hero>
</div>
8.列表渲染
基本示例
<table width="100%">
<tr>
<th align="left">id</th>
<th align="left">name</th>
<th align="left">index</th>
<th align="left">isFirst</th>
<th align="left">isLast</th>
<th align="left">odd</th>
<th align="left">even</th>
</tr>
<tr *ngFor="let item of list; let i=index;let f=first;let l=last;let e=even;let o=odd; trackBy: trackById" [class.odd]="o">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{i}}</td>
<td>{{f}}</td>
<td>{{l}}</td>
<td>{{o}}</td>
<td>{{e}}</td>
</tr>
</table>
export class AppComponent {
list = [
{id:6,name:"name-a"},
{id:5,name:"name-b"},
{id:4,name:"name-c"},
{id:3,name:"name-d"},
{id:2,name:"name-e"},
{id:1,name:"name-f"}
]
trackById(index: number, item: any): number { return item.id; }
}
内置变量
index(number)
引索first(boolean)
是否第一行last(boolean)
是否第二行 even(boolean)
引索是否是偶数,不是指是否偶数行
odd(boolean)
引索是否是奇数,不是指是否奇数行
以上内置变量,必须要在ngFor
内显示声明
,否则默认是拿不到的
ngFor
的内容并不是模板表达式
,而是Angular中的微语法
let item of list; index as i;first as f;last as l;even as e;odd as o;
let item of list; let i=index;let f=first;let l=last;let e=even;let o=odd;
上面,2个写法是等价的,1更短,2更直观,推荐2
trackBy
需要指定一个function
,用于提升性能,提高元素的复用性,避免将所有列表单元重绘。
与vue
中的track-by
或:key
同一个作用
9.事件处理
安利一篇文章,感觉总结的不错
探索至此,发现有一个梳理得非常棒的的博客,强烈安利:Angular 4.x 修仙之路
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。