html是angular模板的语言。几乎所有的html语法都是有效的模板语法。需要注意两点
<script>
元素,被禁用了,以阻止脚本注入的风险。<html>
<body>
<base>
没有任何作用。在angular中,使用双花括号{{}}
来表达插值表达式,插值表达式可以把计算后的字符串插入到html元素标签内或对元素的属性进行赋值。
1 2 | <p> My favorite hero is {{myHero}}.</p> <img src="{{heroImageUrl}}" style="height:30px"/> |
angular对所有花括号中的表达式求值,把求值的结果转换为字符串,并把它们跟相邻的字符串字面量结合起来,然后把结果赋给元素或指令的属性。
1 2 3 4 | getSecond (): number { return 365 * 24 * 60 * 60; } <span>{{ '一年有' + 365 * 24 + '小时,有' + getSecond() + '秒'}}</span> |
看起来很像JavaScript,但并不是全部的JavaScript表达式都适用,有些可能引发副作用的表达式是被禁止的,包括
=
,+=
,-=
, ...
)new
运算符;
或,
的链式表达式|
和&
表达式上下文就是这个组件的实例,是各种绑定值的来源。
表达式中的上下文变量是由“模板变量”、“指令的上下文变量”和“组件的成员”叠加而成的。如果引用的变量命名有冲突,那么模板变量是最优先的。
表达式中变量优先级:模板变量>指令上下文变量>组件成员
模板表达式不能使用全局命名空间的任何东西,比如window
或document
。他们也不能调用console.log
或Math.max
等。只能应用表达式上下文中的成员。
表达式是一个双刃剑,用好了可以成就一个应用,用不好就会毁掉一个应用,所以需要遵循下列规则:
模板语句是用来响应由绑定目标出发的事件,出现在=
的右侧。
1 | <button (click)="myHero = 'iron man'; myHero = myHero.toUpperCase()">确定</button> |
模板语句和插入表达式类似,不同的地方是,模板语句有副作用,因为我们要根据用户的输入来改变应用的状态;而且,模板语句支持基本的赋值和表达式链; or ,
。
1 | <button (click)="myHero += myHero.toUpperCase()">确定</button> // error,不可使用操作并赋值。 |
数据绑定是一种机制,用来协调用户所见和应用数据。只要简单的在绑定源和目标html元素之间声明绑定,我们就可以从html中拉取数据或往html中推送值。
绑定的类型可以根据数据流的的方向分为三类:
[target]='expression'
, bind-target='expression'
(target)='expression'
, on-target='expression'
,[(target)]='expression'
, bindon-target='expression'
模板绑定是通过property
和事件来工作的,而不是attribute
。
attribute和property的差别是,attribute是html定义的,property是由DOM定义的。attribute初始化DOM property,attribute不可变,property可变。
数据绑定的目标是DOM中的某些东西,这个目标可能是元素、组件、指令的prototype,元素、组件、指令的事件,或者是attribute名。
属性绑定
最常用的属性绑定是把元素的属性设置为组件属性的值。
1 2 3 4 | // 插值表达式 <img src="{{heroImg}}" style="width: 100px;" /> // 属性绑定 <img [src]="heroImg" style="width: 100px;" /> |
可以用属性绑定的方式,来给自定义组件设置魔性属性,也即是父组件和子组件通讯的重要途径。
1 | <app-hero-detail [hero]="currentHero"></app-hero-detail> |
属性绑定也常被称作单向输入,因为值的流向是单向的,从数据源到视图。除过方括号[]
,也可以使用bind-
前缀的方式。
1 | <img bind-src="heroImg" style="width: 100px;" /> |
如果忘记了方括号[]
,angular会把这个表达式当做字符串常量来看待,并用该字符串来初始化目标属性,不会计算这个字符串。
attribute、class和style绑定
模板语法为那些不太适合用属性绑定的场合提供了专门的单向数据绑定形式。
attribute绑定
当元素没有属性可绑的时候,就必须使用attribute绑定。
attribute绑定的方法和属性绑定的方法类似,但是方括号中的部分是由attr.
作为前缀和attribute名字组成的。
1 | <tr><td [attr.colspan]="1 + 1">Three-Four</td></tr> |
css,class类绑定
绑定方式为[class.class-name]
。绑定到特定的类,当模板表达式求值为真时,angular会添加这个类,否则会移除这个类。
1 2 | // 点击事件发生后,会移除bad这个类。 <button (click)="myHero = null;" [class.bad]="!myHero" prefix="myHero">确定</button> |
当然,可以使用内置指令ngClass
来管理多个类名。
style,样式绑定
通过样式绑定,可以设置内联样式。绑定语法:[style.style-property]
。如果样式中带有单位,可以根据条件使用[style.style-property.unit]
。
1 2 3 4 5 6 7 | <li *ngFor="let hero of heroes"> <span [style.background-color]="hero.name == myHero ? 'red' : 'none'">{{hero.zh_name}}: {{hero.name}}</span> <img src="{{heroImg || 'http://qiniu.tcs-y.com/trigonometric.png'}}" [style.width.px]="100" [style.border]="'1px solid #ccc'" [style.border-radius.px]="4" /> </li> |
当然,可以使用内置指令ngStyle
来同时设置多个内联样式。
事件绑定
事件绑定的数据流是反向的:从视图到数据源。
事件绑定语法由等号左侧的圆括号目标时事件和右侧引号中的模板语法组成:
1 2 | <button (click)="onSave()">Save</button> <button on-click="onSave()">Save</button> |
圆括号中的名称标记出了目标事件,这里是点击click
。也可以使用规范形式:on-click
。
在事件绑定中,angular会为目标事件设置事件处理器。绑定会通过名叫$event
的事件对象传递关于此事件的信息。
1 | <button (click)="clickEvent($event)" prefix="myHero">确定</button> |
双向数据绑定
在元素层面上,既要设置元素属性,又要监听元素事件变化。语法为[(x)]
,结合了属性绑定[]
和事件绑定()
。
一般在使用组件的时候,父子组件通信的时,不仅需要从父组件传递到子组件,也希望子组件传递到父组件的时候,双向绑定是很有用的。
我们也希望能在<input>
和<select>
这样的html表单元素上使用双向数据绑定,但是原生html元素不支持这种方式,我们可以用ngModle
指令来达到在元素上使用双向绑定。
模板引用变量通常用来引用模板中的某个DOM元素,也可以引用angular的组件或指令。
使用井号(#)来声明引用变量,模板引用变量的作用域是整个模板,不要在同一个模板中多次定义同一个变量名,否则在运行期间无法确定它的值。例如:
1 2 | <button (click)="heroAbility.walk()">前进 {{hero.name}}</button> <app-hero-ability [hero]="hero" #heroAbility></app-hero-ability> |
使用heroAbility
来引用组件app-hero-ability
。然后在按钮上绑定引用组件的方法walk
。
绑定的目标是在等号=
的左侧,数据源是在等号=
右侧。目标是绑定符[]
、()
或[()]
中的属性或事件名,源则是等号右侧引号""
中的部分或插值表达式{{}}中的部分。
当组件的属性作为绑定的目标的时候,需要明确的标记为输入属性或输出属性。
可以通过装饰器标记输入输出属性:
1 2 3 | import { Component, EventEmitter, Input, Output } from '@angular/core'; @Input() size: number | string; @Output() sizeChange = new EventEmitter<number>(); |
也可以在指令元数据的inputs
或outputs
数组中标记处这些成员:
1 2 3 4 5 | @Component({ selector: 'app-sizer', inputs: ['size'], outputs: ['sizeChange'], }) |
但是建议是使用通过装饰器标记输入输出属性,因为这样更易读。
可以给输入输出属性指定个别名:
1 2 | // mySize就是指令size的别名 @Input('mySize') size: number | string; |
模板表达式使用了JavaScript语法的子集,并补充了几个用于特殊场景的特殊操作符,常用的有:
管道是一个简单的函数,接受一个输入值并返回转换的结果,对于小型的转换来说特别方便,很容易用于模板表达式中,用法:{{explain | xxx}}
,xxx就是管道函数。也可以使用多个管道串联,依次向右。
1 | <span>Number:{{i + 1}} {{hero.zh_name}}: {{hero.name | uppercase | json}}</span> |
还可以使用参数:
1 | <div>Birthdate: {{currentHero?.birthdate | date:'longDate'}}</div> |
安全导航操作符?.
是一种流利而便利的方式,用于保护出现子安属性路径中的null和undefined,以避免JavaScript直接奔溃抛出异常。
比如没有选中英雄的时候,selectHero
是空,这时候访问selectHero.name
是会抛出异常的,为了处理,我们需要判断下,先判断了selectHero
然后再判断selectHero
:
1 2 3 4 5 6 7 8 | <div *ngIf="selectHero && selectHero.name"> <h2>选中的英雄的技能:</h2> <div [ngSwitch]="selectHero.name"> <div *ngSwitchCase="'Iron Man'">激光炮</div> <div *ngSwitchDefault>敬请期待</div> </div> </div> |
是的,这样是有效,但是闲的臃肿和累赘,要是多级属性,那就简直是噩梦:
我们可以使用安全导航操作符来简化下:
1 2 3 4 5 6 7 8 | <div *ngIf="selectHero?.name"> <h2>选中的英雄的技能:</h2> <div [ngSwitch]="selectHero.name"> <div *ngSwitchCase="'Iron Man'">激光炮</div> <div *ngSwitchDefault>敬请期待</div> </div> </div> |