Property binding(属性绑定)
最常见的属性绑定就是将元素的属性设置为组件的属性值:
<img [src]="heroImageUrl">www.saixinyi.com
<button [disabled]="isUnchanged">Cancel is disabled</button>
或者是设置指令的属性:
<div [ngClass]="classes">[ngClass] binding to the classes property</div>
或者是自定义组件的模型属性www.saixinyi.com(也是父子组件交互的一种方式):
<hero-detail [hero]="currentHero"></hero-detail>
属性绑定是单向数据绑定,因为数据是从组件的属性流向目标元素的属性。
绑定目标
中括号内的名称指定了绑定的目标属性,例如设置img的src属性:
<img [src]="heroImageUrl">
除了中括号,还可以使用bind-
前缀:www.saixinyi.com
<img bind-src="heroImageUrl">
目标名称总是property名称。我们看到src
会认为它是attribute名称,实际上是img元素property的名称。
元素属性是最常见的目标,但是Angular会先在已知的指令中查找是否有该名称的属性。如果在已知指令或元素的属性都没有找到该名称的属性,Angular会返回一个unknown directive
错误。
使用属性绑定时,要注意模板表达式返回正确的数据类型,也就是说模板表达式计算得到的值的类型应该和目标属性值类型一样。
别忘了属性绑定的中括号。如果忘记了中括号,Angular会把字符串作为常量,并且用该字符串初始化目标属性,而不是计算字符串的值。
<!--www.saixinyi.com
BAD!
HeroDetailComponent.hero expects a Hero object,
not the string "currentHero"
-->
<hero-detail hero="currentHero"></hero-detail>
这种属性初始化方式对组件或指令也是适用的。例如将HeroDetailComponent
组件的prefix
属性初始化为一个固定的字符串:
<hero-detail prefix="You are my" [hero]="currentHero"></hero-detail>
选择属性绑定还是插值绑定
通常我们在属性绑定和插值绑定之间二选一,它们俩的作用是一样的:
Interpolated: <img src="{{heroImageUrl}}"><br>
Property bound: <img [src]="heroImageUrl">
<div>The interpolated title is {{title}}</div>
<div [textContent]="'The [textContent] title is '+title"></div>
事实上Angular在渲染视图之前会吧插值绑定转换成属性绑定。
Attribute, class, and style binding
模板语法还提供了一些特殊的单向绑定。
Attribute binding
我们可以使用attribute绑定来设置attribute的值。
上一篇已经说了attribute和property的区别。为什么Angular还要提供attribute绑定呢?因为有个attribute没有对应的property。例如aria,svg,table的span,它们是纯attribute,没有对应的元素属性。
如果我们这样做:
<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
会得到这样的错误:
Template parse errors:
Can't bind to 'colspan' since it isn't a known native property
因为<td>
元素没有名为colspan
的property。它有colspan
attribute,但是插值绑定和property绑定只会设置property,而不是attribute。
因此,我们需要attribute绑定。
attribute绑定跟property绑定是相似的。只是中括号内不是property名称,而是加了前缀attr.
的attribute名称。
<tr><td [attr.colspan]="1 + 1">One-Two</td>www.saixinyi.com</tr>
最主要的使用场景就是设置ARIA属性:
<!--www.saixinyi.com create and set an aria attribute for assistive technology -->
<button [attr.aria-label]="actionName">{{actionName}} with Aria</button>
Class binding
我们使用class绑定来为元素的class属性添加或移除CSS类。
class绑定也是跟property绑定相似的。中括号内是class
或者是加了前缀class.
的CSS类名称:[class]
、[class.class-name]
。
[class]
这种方式是根据模板表达式的值添加或者移除所有的CSS类。
<!-- reset/override all class names with a binding -->
<div class="bad curly special"
[class]="badCurly">Bad curly</div>
也可以指定类名:
<!-- toggle the "special" class on/off with a property -->
<div [class.special]="isSpecial">The class binding is special</div>
<!-- binding to `class.special` trumps the class attribute -->
<div class="special"
[class.special]="!isSpecial">This one is not so special</div>
如果要同时管理多个CSS类,更好的做法是使用NgClass
指令。
Style binding
我们可以使用style绑定来设置内联样式。
style绑定也是跟property绑定相似的。中括号内是加了前缀style.
的CSS样式属性名称:[style.style-property]
。
<button [style.color] = "isSpecial ? 'red' : 'green'">Red</button>
<button [style.backgroundColor]="canSave ?'cyan' : 'grey'" >Save</button>
有的style绑定有单位。
<button [style.fontSize.em]="isSpecial ? 3 : 1" >Big</button>
<button [style.fontSize.%]="!isSpecial ? 150 : 50" >Small</button>
如果要同时管理多个内联样式,更好的做法是使用NgStyle
指令。
Event binding(事件绑定)
事件绑定是从视图到组件的单向数据绑定。
事件绑定语法由等号左边的放在小括号内的目标事件和等号右边的放在引号内的模板声明两部分组成组成。例如将按钮的点击事件绑定到组件的onSave()
方法:
<button (click)="onSave()">Save</button>
目标事件
小括号内的名称表明目标事件。也可以使用on-
前缀,这是标准形式:
<button on-click="onSave()">On Save</button>
元素的事件是最常见的绑定目标,但是Angular会先查找已知的指令是否有该名称的事件属性。
<!-- `myClick` is an event on the custom `MyClickDirective` -->
<div (myClick)="clickMessage=$event">click with myClick</div>
如果没有匹配的元素事件或已知指令的事件属性,Angular会返回unknown directive
错误。
$event和事件处理声明
在事件绑定中,Angular为目标事件设置事件处理器。当事件触发了,事件处理器会执行模板声明。
事件绑定使用一个名为$event
的对象承载事件的相关信息,包括数据值。$event
对象的类型取决于目标事件。如果目标事件是DOM元素事件,那$event
就是DOM事件对象,有诸如target
和target.value
这样的属性。
例如:
<input [value]="currentHero.firstName"
(input)="currentHero.firstName=$event.target.value" >
如果绑定事件属于指令(记住,组件也是指令哦),那么$event
取决于指令。
使用EventEmitter自定义事件
指令通常使用EventEmitter来触发自定义事件。每个指令会创建一个EventEmitter
并将它作为属性暴露出来。组件调用EventEmitter.emit(payload)
来发起一个事件,并传入一个消息。父指令通过绑定该属性监听该事件,并通过$event
对象访问传递的参数payload
。
例如,HeroDetailComponent
展示hero的信息并响应用户的操作。尽管HeroDetailComponent
有个删除按钮,但是它不知道如何删除一个hero。最好的做法是它发起一个事件来处理用户的删除请求。
HeroDetailComponent
的部分代码如下:
template: `
<div>
<img src="{{heroImageUrl}}">
<span [style.text-decoration]="lineThrough">
{{prefix}} {{hero?.fullName}}
</span>
<button (click)="delete()">Delete</button>
</div>`
// This component make a request but it can't actually delete a hero.
deleteRequest = new EventEmitter<Hero>();
delete() {
this.deleteRequest.emit(this.hero);
}
假设一个托管父组件绑定了HeroDetailComponent
的deleteRequest
事件:
<hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero"></hero-detail>
当发起deleteRequest
时,Angular调用父组件的deleteHero
方法,使用$event
变量传递要删除的hero。