2

原文链接

Angular官方开发文档讲的很细致,但是这是一把双刃剑。

双刃剑

上一篇中Angular CLI的文档,并非Angular官方开发文档的一部分。(可能我没发现吧)
而从这篇文章开始,我将开始阅读Angular的官方开发文档

首先,我阅读完了教程核心知识-构架,粗略的了解下Angular的开发流程和一些基本的概念。

然后,就是看核心知识-组件与模板了,看完了第一遍其实,还是有点懵。

有几点感受:

  1. 可能是因为是英译,有些地方感觉翻译的有点不够接地气,不太便于国人理解
  2. 文字过于冗长,有时候让人很难抓到重点
  3. 像看小说,一定要循序渐进慢慢看,不然稍微跳一下,剧情就接不上了
  4. 看过的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举例
上面这个例子中,imgattributesrc与imagepropertysrc相同,所有感觉让人产生混淆

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-classd-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 Events Bubbling》

探索至此,发现有一个梳理得非常棒的的博客,强烈安利:Angular 4.x 修仙之路

MichaelZ
374 声望13 粉丝

web全栈工程师


引用和评论

0 条评论