您可使用Class绑定从元素的类属性添加和删除CSS类名称。html
Class绑定语法相似于属性(property)绑定。 之前缀类开始,可选地跟一个点(.)和一个CSS类的名字替代括号内的元素属性:[class.class-name]。java
如下示例显示如何使用class绑定来添加和删除应用程序的“special”类。 如下是如何设置没有绑定的属性:web
<!-- standard class attribute setting --> <div class="bad curly special">Bad curly special</div>
你能够用一个绑定到所需的类名称的字符串替换它;这是一个全或无,替代绑定。express
<!-- reset/override all class names with a binding --> <div class="bad curly special" [class]="badCurly">Bad curly</div>
你能够用一个绑定到所需的类名称的字符串替换它;这是一个全或无的替代绑定。编程
<!-- reset/override all class names with a binding --> <div class="bad curly special" [class]="badCurly">Bad curly</div>
最后,你能够绑定到一个specific类名。 当模板表达式计算结果为true时,Angular会添加类。 当表达式为false时,它将删除类。json
<!-- 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>
虽然这是切换单个类名的好方法,可是在同时管理多个类名时一般首选NgClass指令。api
您可使用Style绑定来设置内联样式。安全
样式绑定语法相似于属性(property)绑定。之前缀样式开始,后跟一个点(.)和一个CSS样式属性的名称代替括号内的元素属性,:[style.style-property]。服务器
<button [style.color]="isSpecial ? 'red': 'green'">Red</button> <button [style.background-color]="canSave ? 'cyan': 'grey'" >Save</button>
一些样式绑定样式有一个单位扩展名。 如下示例有条件地将字体大小设置为“em”和“%”单位。app
<button [style.font-size.em]="isSpecial ? 3 : 1" >Big</button> <button [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button>
虽然这是设置单个样式的好方法,可是在同时设置多个内联样式时,一般首选NgStyle指令。
请注意,样式属性名称能够用dash-case(如上所示)或camelCase(如fontSize)书写。
样式属性命名
虽然在AngularDart中camelCase和dash-case风格的属性命名方案是等价的,但只有dash-case命名法才能被dash:html包中CssStyleDeclaration的方法getPropertyValue()和setProperty()识别。 所以,使用样式属性名称的dash-case一般是首选。
到目前为止,您所遇到的绑定指令能够在一个方向上流动数据:从一个组件到一个元素。
用户不仅是盯着屏幕。 他们在输入框中输入文字。 他们从列表中选择项目。 他们点击按钮。 这样的用户操做可能致使数据流向相反的方向:从元素到组件。
了解用户操做的惟一方法是侦听某些事件,例如按键,鼠标移动,点击和触摸。 您经过Angular事件绑定声明您对用户操做的兴趣。
事件绑定语法由等号左边括号内的目标事件名称和右边带引号的模板语句组成。 如下事件绑定侦听按钮的单击事件,每当发生点击时调用组件的onSave()方法:
<button (click)="onSave()">Save</button>
圆括号之间的名称 - 例如(click) - 标识目标事件。 在如下示例中,目标是按钮的单击事件。
<button (click)="onSave()">Save</button>
有些人更喜欢使用前缀on-替代方法,称为规范形式:
<button on-click="onSave()">On Save</button>
元素事件多是更常见的目标,但Angular首先查看名称是否匹配已知指令的事件属性,以下例所示:
<!-- `myClick` is an event on the custom `ClickDirective` --> <div (myClick)="clickMessage=$event" clickable>click with myClick</div>
有关别名输入/输出属性的部分将进一步介绍myClick伪指令。
若是名称未能匹配已知指令的元素事件或输出属性,则Angular会报告“未知指令”错误。
在事件绑定中,Angular为目标事件设置了一个事件处理程序。
事件发生时,处理程序执行模板语句。 模板语句一般包含一个接收器,它响应事件执行一个动做,例如将HTML控件的值存储到模型中。
绑定经过一个名为$event的事件对象来传递关于该事件的信息,包括数据值。
事件对象的形状由目标事件决定。 若是目标事件是原生DOM元素事件,那么$event是一个DOM事件对象,具备诸如target和target.value的属性。
思考这个例子:
<input [value]="currentHero.name" (input)="currentHero.name=$event.target.value" >
此代码经过绑定到name属性来设置输入框value属性。 要监听值的更改,代码会绑定到输入框的输入事件。 当用户进行更改时,将引起输入事件,绑定在包含DOM事件对象$event的上下文中执行语句。
要更新name属性,能够经过路径$event.target.value来检索已更改的文本。
若是事件属于指令(回想组件是指令),则$event具备指令的全部能力。
指令一般使用StreamController来引起自定义事件。 该指令建立一个StreamController并将其stream做为属性公开。 该指令调用StreamController.add(payload)来触发一个事件,传递一个消息,能够是任何东西。 父指令经过绑定监听此属性并经过$event对象访问内容。payload:承载数据
考虑一个呈现英雄信息并响应用户操做的HeroDetailComponent。 虽然HeroDetailComponent有一个删除按钮,但不知道如何删除英雄自己。 最好的办法是触发一个事件,报告用户的删除请求。
如下是HeroDetailComponent的相关摘录:lib/src/hero_detail_component.dart (template)
template: ''' <div> <img src="{{heroImageUrl}}"> <span [style.text-decoration]="lineThrough"> {{prefix}} {{hero?.name}} </span> <button (click)="delete()">Delete</button> </div> ''',
lib/src/hero_detail_component.dart (deleteRequest)
final _deleteRequest = new StreamController<Hero>(); @Output() Stream<Hero> get deleteRequest => _deleteRequest.stream; void delete() { _deleteRequest.add(hero); }
组件定义了一个私有的StreamController属性,并经过deleteRequest获取器公开控制器的stream。 当用户点击Delete时,组件的delete()方法被调用,指示StreamController将Hero添加到stream中。
如今想象一个托管的父组件绑定到HeroDetailComponent的deleteRequest事件。
<hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero"></hero-detail>
当触发deleteRequest事件时,Angular调用父组件的deleteHero方法,传递$event变量中的hero-to-delete(由HeroDetail发出)。
deleteHero方法有一个附做用:删除一个英雄。 模板语句的附做用不仅是好的,但可预期。
删除英雄更新模型,可能会触发其余更改,包括查询并保存到远程服务器。 这些变化经过系统渗透,并最终显示在相关视图。
您常常但愿显示数据属性,并在用户进行更改时更新该属性。
元素另外一方面为元素更改事件组合设置特定元素属性和监听。
Angular为此提供了一个特殊的双向数据绑定语法, [(x)]. [(x)]语法将属性绑定的方括号[x]与事件绑定的圆括号(x)组合在一块儿。
[()] =香蕉盒
在一个盒子里形象化一个香蕉,记住圆括号在括号内。
当元素有一个名为x的可设置属性和一个名为xChange的对应事件时,[(x)]语法很容易演示。 这是一个适合模式的SizerComponent。 它有一个size值属性和一个伴随sizeChange事件:
lib/src/sizer_component.dart
import 'dart:async'; import 'dart:math'; import 'package:angular/angular.dart'; const _minSize = 8; const _maxSize = _minSize * 5; @Component( selector: 'my-sizer', template: ''' <div> <button (click)="dec()" [disabled]="size <= minSize">-</button> <button (click)="inc()" [disabled]="size >= maxSize">+</button> <label [style.font-size.px]="size">FontSize: {{size}}px</label> </div>''', ) class SizerComponent { // TODO: under Angular 4 we will be able to just export the const final int minSize = _minSize, maxSize = _maxSize; int _size = _minSize * 2; int get size => _size; @Input() void set size(/*String|int*/ val) { int z = val is int ? val : int.parse(val, onError: (_) => null); if (z != null) _size = min(maxSize, max(minSize, z)); } final _sizeChange = new StreamController<int>(); @Output() Stream<int> get sizeChange => _sizeChange.stream; void dec() => resize(-1); void inc() => resize(1); void resize(int delta) { size = size + delta; _sizeChange.add(size); } }
初始size是来自属性绑定的输入值。 单击按钮可在最小/最大值限制内增长或减少size,而后用调整的大小触发(发出)sizeChange事件。
下面是一个示例,其中AppComponent.fontSizePx是双向绑定到SizerComponent的:
<my-sizer [(size)]="fontSizePx" #mySizer></my-sizer> <div [style.font-size.px]="mySizer.size">Resizable Text</div>
AppComponent.fontSizePx创建初始SizerComponent.size的值。 单击按钮经过双向绑定更新AppComponent.fontSizePx。 修改后的size值流向样式绑定,使显示的文本变大或变小。
双向绑定语法实际上只是属性(property)绑定和事件绑定的语法糖。 Angular desugars将SizerComponent绑定到这里:
<my-sizer [size]="fontSizePx" (sizeChange)="fontSizePx=$event"></my-sizer>
$event变量包含SizerComponent.sizeChange事件的有效数据。 当用户单击按钮时,Angular将$event值分配给AppComponent.fontSizePx。
显然,与单独的属性和事件绑定相比,双向绑定语法至关方便。
使用HTML表单元素(如<input>和<select>)的双向绑定会很方便。 可是,没有原生HTML元素遵循x值和xChange事件模式。
幸运的是,Angular NgModel指令是一个使元素造成双向绑定的桥梁。
早期版本的Angular包含了七十多个内置指令。 社区贡献了更多,而且为内部应用程序建立了无数私人指令。
在Angular中你不须要这些指令。 一般,您可使用功能更强大,表现力更强的Angular绑定系统得到相同的结果。 当你能够写一个简单的绑定时为何要建立一个指令来处理点击呢?
<button (click)="onSave()">Save</button>
您仍然能够从简化复杂任务的指令中受益。 Angular仍然附带内置的指令; 只是没有那么多。 你将会写你本身的指令,只是很少。
该部分回顾了一些最经常使用的内置指令,归类为属性(attribute)指令或结构指令。
属性指令监听并修改其余HTML元素,属性(attribute),属性(property)和组件的行为。 它们一般应用于元素,就好像它们是HTML属性同样,所以也就是名称。
属性指令指南中介绍了许多细节。 许多Angular包(如Router和Forms包)都定义了本身的属性指令。 本节介绍最经常使用的属性指令:
您一般经过动态添加和删除CSS类来控制元素的显示方式。 你能够绑定到ngClass来同时添加或删除多个类。
class绑定是添加或删除单个类的好方法。
<!-- toggle the "special" class on/off with a property --> <div [class.special]="isSpecial">The class binding is special</div>
要同时添加或删除许多CSS类,NgClass指令多是更好的选择。
尝试绑定ngClass到一个key:value 控制Map。 对象的每一个键都是一个CSS类的名字; 若是应该添加类,则其值为true,若是应该删除则为false。
考虑一个设置组件属性的组件方法setCurrentClasses,currentClasses,该对象基于三个其余组件属性的true / false状态添加或删除三个类:
Map<String, bool> currentClasses = <String, bool>{}; void setCurrentClasses() { currentClasses = <String, bool>{ 'saveable': canSave, 'modified': !isUnchanged, 'special': isSpecial }; }
将ngClass属性绑定添加到currentClasses,相应地设置元素的类:
<div [ngClass]="currentClasses">This div is initially saveable, unchanged, and special</div>
由您决定最初调用setCurrentClasses(),以及什么时候依赖属性发生更改。
您能够根据组件的状态动态设置内联样式。 使用NgStyle,您能够同时设置多个内联样式。
样式绑定是设置单个样式值的简单方法。
<div [style.font-size]="isSpecial ? 'x-large' : 'smaller'" > This div is x-large or smaller. </div>
要同时设置多个内联样式,NgStyle指令多是更好的选择。
尝试绑定ngStyle到一个key:value控制Map。 对象的每一个键都是一个样式名称;它的值是适合那种样式。
考虑一个设置组件属性的组件方法setCurrentStyles,currentStyles,一个定义了三种样式的对象,基于另外三个组件属性的状态:
Map<String, String> currentStyles = <String, String>{}; void setCurrentStyles() { currentStyles = <String, String>{ 'font-style': canSave ? 'italic' : 'normal', 'font-weight': !isUnchanged ? 'bold' : 'normal', 'font-size': isSpecial ? '24px' : '12px' }; }
添加ngStyle属性绑定到currentStyles将相应地设置元素的样式:
<div [ngStyle]="currentStyles"> This div is initially italic, normal weight, and extra large (24px). </div>
由您决定最初调用setCurrentStyles(),以及什么时候依赖属性发生更改。
在开发数据输入表单时,一般都会显示数据属性,并在用户进行更改时更新该属性。
使用NgModel指令进行双向数据绑定使得这一切变得简单。 这是一个例子:
<input [(ngModel)]="currentHero.name">
回顾一下名称绑定,请注意,您能够经过单独绑定<input>元素的value属性和输入事件来得到相同的结果。
<input [value]="currentHero.name" (input)="currentHero.name=$event.target.value" >
这很麻烦。 谁能够记住要设置哪一个元素属性以及哪一个元素事件发出用户更改? 如何从输入框中提取当前显示的文本,以便更新数据属性? 谁想每一次都看看?
该ngModel指令隐藏了本身的ngModel输入属性和ngModelChange输出属性背后的这些繁重的细节。
<input [ngModel]="currentHero.name" (ngModelChange)="currentHero.name=$event">
ngModel 数据属性设置元素的值属性,ngModelChange事件属性监听元素值的变化。
细节是特定于每种元素,所以NgModel指令只适用于ControlValueAccessor支持的元素以使元素适配这个协议。<input>框是其中的一个元素。 Angular为全部基本的HTML表单元素提供值访问器,Forms指南展现了如何绑定到它们。
您不能将[(ngModel)]应用到非表单原生元素或第三方自定义组件,除非您编写了一个合适的值存取器,这个技术超出了本指南的范围。
您不须要为您编写的Angular组件添加值存取器,由于您能够将值和事件属性命名为适合Angular基本的双向绑定语法,并彻底跳过NgModel。上面显示的sizer是这种技术的一个例子。
单独的ngModel绑定是对绑定到元素原生属性的改进。 你能够作得更好。
你不该该提到数据属性两次。 Angular应该可以捕获组件的数据属性,并使用[(ngModel)]语法将其设置为一个声明:
<input [(ngModel)]="currentHero.name">
[(ngModel)]是你须要的吗? 是否有理由回到扩展的形式?
[(ngModel)]语法只能设置数据绑定属性。 若是您须要作更多或不一样的事情,您能够编写扩展表单。
如下人为的例子强制输入值为大写:
<input [ngModel]="currentHero.name" (ngModelChange)="setUppercaseName($event)">
这里有全部的变化,包括大写版本:
结构指令负责HTML布局。 它们一般经过添加,删除和操做它们所链接的主机元素来对DOM的结构进行调整或重塑。
“结构指令”指南介绍告终构指令的深刻细节,您将在其中学习如下内容:
本节介绍常见的结构指令:
您能够经过向该元素应用NgIf指令(称为宿主元素)来添加或移除DOM中的元素。 在此示例中,将指令绑定到条件表达式,如isActive。
<hero-detail *ngIf="isActive"></hero-detail>
不要忘记ngIf前面的星号(*)。
非true/false的值
当isActive表达式返回true值时,NgIf将HeroDetailComponent添加到DOM。 当表达式为false时,NgIf从DOM中删除HeroDetailComponent,销毁该组件及其全部子组件。
在Dart模式下,Dart指望布尔值(类型为bool的)为true或false。 即便在生产模式中,Dart惟一真实的是true, 全部其它值是false。 另外一方面,TypeScript和JavaScript将许多值(包括非空对象)视为true。 例如,TypeScript Angular程序一般具备诸如* ngIf =“currentHero”的代码,其中Dart程序具备诸如* ngIf =“currentHero!= null”之类的代码。
将TypeScript代码转换为Dart代码时,请注意真/假问题。 例如,忘记!= null可能会致使检查模式中的异常,例如“EXCEPTION:type ‘Hero’ is not a subtype of type ‘bool’ of ‘boolean expression’”. 有关更多信息,请参阅Dart语言导览中的布尔值。
Dart 2.0注意:检查模式不会在飞镖2.0。 有关更多信息,请参阅Dart 2.0更新。
您可使用类或样式绑定来控制元素的可见性:
<!-- isSpecial is true --> <div [class.hidden]="!isSpecial">Show with class</div> <div [class.hidden]="isSpecial">Hide with class</div> <!-- HeroDetail is in the DOM but hidden --> <hero-detail [class.hidden]="isSpecial"></hero-detail> <div [style.display]="isSpecial ? 'block' : 'none'">Show with style</div> <div [style.display]="isSpecial ? 'none' : 'block'">Hide with style</div>
隐藏一个元素与用NgIf去除一个元素是彻底不一样的。
当你隐藏一个元素时,该元素及其全部的后代仍然保留在DOM中。 这些元素的全部组件都保留在内存中,Angular可能会继续检查更改。 您的应用可能会占用至关可观的计算资源,会下降用户不可见的性能。
当NgIf为false时,Angular从DOM中删除元素及其后代。 它摧毁了他们的组件,潜在地释放了大量的资源,从而带来了更加快速的用户体验。
展现/隐藏技术适合少数几个后代的元素。 警戒隐藏大型组件树; NgIf多是更安全的选择。
ngIf指令一般用于防止null。 显示/隐藏是无用的。 若是嵌套表达式试图访问null属性,Angular会抛出一个错误。
这里咱们看到NgIf守护两个<div>。 currentHero名称仅在有currentHero时出现。 nullHero从不显示。
<div *ngIf="currentHero != null">Hello, {{currentHero.name}}</div> <div *ngIf="nullHero != null">Hello, {{nullHero.name}}</div>
另请参阅下面描述的安全导航操做符。
NgFor是一个迭代指令 - 一种呈现项目列表的方式。 您能够定义一个HTML块来定义应该如何显示单个项目。 您告诉Angular将该块用做呈现列表中每一个项目的模板。
下面是NgFor应用于<div>的例子:
<div *ngFor="let hero of heroes">{{hero.name}}</div>
您也能够将NgFor应用于组件元素,以下例所示:
<hero-detail *ngFor="let hero of heroes" [hero]="hero"></hero-detail>
不要忘记ngFor前面的星号(*)。
分配给* ngFor的文本是指导迭代器进程的指令。
分配给* ngFor的字符串不是模板表达式。 这是一种微语法 - Angular解释的一种小语言。 字符串“let hero of heroes”是指:
取英雄列表中的每一个英雄,将其存储在本地英雄循环变量中,并使其可用于每次迭代的模板HTML。
Angular把这条指令翻译成一个围绕宿主元素的<template>,而后重复使用这个模板为列表中的每一个英雄建立一组新的元素和绑定。
在“结构指令”指南中了解微语法。
hero以前的let关键字建立一个名为hero的模板输入变量。 ngFor指令迭代由父组件的heroes属性返回的heroes,并在每次迭代期间将hero设置为列表中的当前项目。
要访问hero的属性,请参考ngFor宿主元素(或其后代内)中的hero输入变量。在这里,英雄首先在插值中被引用,而后传递给<hero-detail>组件的hero属性绑定。
<div *ngFor="let hero of heroes">{{hero.name}}</div> <hero-detail *ngFor="let hero of heroes" [hero]="hero"></hero-detail>
在“结构指令”指南中了解有关模板输入变量的更多信息。
NgFor指令上下文的index属性返回每一个迭代中项目的从零开始的索引。 您能够捕获模板输入变量中的index,并在模板中使用它。
下一个示例捕获名为i的变量中的索引,并使用像这样的英雄名称来显示它。
<div *ngFor="let hero of heroes; let i=index">{{i + 1}} - {{hero.name}}</div>
了解其余NgFor上下文值,例如NgFor API参考中的last,even和odd。
NgFor指令可能表现不佳,特别是在大型列表中。 对一个项目,删除项目或添加项目的小改动能够触发DOM操做的级联。
例如,从新查询服务器可能会重置全部新的英雄对象的列表。
大多数,若是不是所有,之前显示的英雄。 你知道这一点,由于每一个英雄的ID没有改变。 可是Angular只能看到新的对象引用列表。 它别无选择,只能拆除旧的DOM元素并插入全部新的DOM元素。
Angular能够经过trackBy避免这种流失。 向组件添加一个返回NgFor应跟踪值的方法。 在这个例子中,这个值就是英雄的ID。
int trackByHeroes(int index, Hero hero) => hero.id;
在微语法表达式中,将trackBy设置为此方法。
<div *ngFor="let hero of heroes; trackBy: trackByHeroes"> ({{hero.id}}) {{hero.name}} </div>
这是trackBy效果的一个例子。 “Reset heroes”用相同的 hero.ids建立新的heroes。 “Change ids”用新的hero.ids创造新的heroes 。
NgSwitch就像Dart switch语句。 它能够根据切换条件从几个可能的元素中显示一个元素。 Angular只把选中的元素放入DOM中。
NgSwitch其实是一组三个协做指令:NgSwitch,NgSwitchCase和NgSwitchDefault,如本例所示。
<div [ngSwitch]="currentHero.emotion"> <happy-hero *ngSwitchCase="'happy'" [hero]="currentHero"></happy-hero> <sad-hero *ngSwitchCase="'sad'" [hero]="currentHero"></sad-hero> <confused-hero *ngSwitchCase="'confused'" [hero]="currentHero"></confused-hero> <unknown-hero *ngSwitchDefault [hero]="currentHero"></unknown-hero> </div>
您可能会遇到旧代码中的NgSwitchWhen指令。 这是NgSwitchCase的弃用名称。
NgSwitch是控制器指令。将其绑定到返回switch值的表达式。本例中的emotion值是一个字符串,可是switch值能够是任何类型。
绑定到[ngSwitch]。 若是您尝试设置*ngSwitch,则会出现错误,由于NgSwitch是一个属性指令,而不是结构指令。 它改变了其同伴指令的行为。 它不直接操做DOM。
绑定到*ngSwitchCase和*ngSwitchDefault。 NgSwitchCase和NgSwitchDefault指令是结构指令,由于它们添加或删除DOM中的元素。
switch指令对于添加和删除组件元素特别有用。本示例在hero_switch_components.dart文件中定义的四个“emotional hero”组件之间进行切换。每一个组件都有一个绑定到父组件的currentHero的英雄输入属性。
switch指令也适用于原生元素和Web组件。 例如,您可使用如下代替<confused-hero>switch选项。
<div *ngSwitchCase="'confused'">Are you as confused as {{currentHero.name}}?</div>
模板引用变量一般是对模板内DOM元素的引用。 它也能够是对Angular组件或指令或Web组件的引用。
使用hash符号(#)来声明一个引用变量。 #phone在<input>元素上声明了一个phone变量。
<input #phone placeholder="phone number">
您能够参考模板中任何位置的模板引用变量。 在<input>上声明的phone变量在模板另外一端的<button>中被使用
<input #phone placeholder="phone number"> <!-- lots of other elements --> <!-- phone refers to the input element; pass its `value` to an event handler --> <button (click)="callPhone(phone.value)">Call</button>
在大多数状况下,Angular将引用变量的值设置为声明的元素。在前面的例子中, phone是指电话号码 <input>框。电话按钮点击处理程序将输入值传递给组件的callPhone方法。可是一个指令能够改变这种行为,并将其值设置为别的东西,好比自己。 NgForm指令这样作。
如下是Forms指南中表单示例的简化版本。
<form (ngSubmit)="onSubmit(heroForm)" #heroForm="ngForm"> <div class="form-group"> <label for="name">Name <input class="form-control" ngControl="name" required [(ngModel)]="hero.name"> </label> </div> <button type="submit" [disabled]="!heroForm.form.valid">Submit</button> </form> <div [hidden]="!heroForm.form.valid"> {{submitMessage}} </div>
在这个例子中,一个模板引用变量heroForm出现三次,被大量的HTML隔开。heroForm的值是什么? heroForm是一个Angular NgForm指令的引用,能够跟踪表单中每一个控件的值和有效性。
原生<form>元素没有form属性。 可是NgForm指令有,它解释了若是heroForm.form.valid无效而且将整个表单控件树传递给父组件的onSubmit方法,您能够禁用提交按钮。
模板引用变量(#phone)与模板输入变量(let phone)不一样,如您在*ngFor中可能看到的那样。 了解“结构指令”指南中的差别。
引用变量的范围是整个模板。 不要在同一模板中屡次定义相同的变量名称。 运行时值将是不可预知的。
你可使用ref-前缀替代#。 本示例将fax变量声明为ref-fax,而不是#fax。
<input ref-fax placeholder="fax number"> <button (click)="callFax(fax.value)">Fax</button>
到目前为止,该页面主要集中在绑定到模板表达式中的组件成员以及出如今绑定声明右侧的语句上。
该位置的成员是数据绑定源。
本节重点讨论对目标的绑定,它们是绑定声明左侧的指令属性。这些指令属性必须声明为输入或输出。
请记住:全部组件都是指令。
请注意数据绑定目标和数据绑定源之间的重要区别。
绑定的目标是在=的左边。 源位于=的右侧。
绑定的目标是绑定标点符号中的属性或事件:[],()或[()]。 源是在引号(“”)内或插值({{}})内。
source指令的每一个成员均可以自动得到绑定。 您没必要在模板表达式或语句中使用任何特殊的操做来访问指令成员。
您对目标指令的成员访问权限有限。 您只能绑定到明确标识为输入和输出的属性。
在下面的代码片断中,iconUrl和onSave是AppComponent的数据绑定成员,而且在等号(=)右侧的引用语法中被引用。
<img [src]="iconUrl"/> <button (click)="onSave()">Save</button>
它们既不是输入也不是输出。 他们是绑定的来源。 目标是本地的<img>和<button>元素。
如今看另外一个片断,其中HeroDetailComponent是equals(=)左边绑定的目标。
<hero-detail [hero]="currentHero" (deleteRequest)="deleteHero($event)"> </hero-detail>
HeroDetailComponent.hero和HeroDetailComponent.deleteRequest都在绑定声明的左侧。 HeroDetailComponent.hero在括号内; 它是一个属性绑定的目标. HeroDetailComponent.deleteRequest在括号内; 它是事件绑定的目标。
目标属性必须明确标记为输入或输出。
在HeroDetailComponent中,这些属性使用注解标记为输入或输出属性。
@Input() Hero hero; final _deleteRequest = new StreamController<Hero>(); @Output() Stream<Hero> get deleteRequest => _deleteRequest.stream;
input属性一般接收数据值。 Output属性公开事件生成器,如Stream对象。
术语input和Output反映了目标指令的视角。
HeroDetailComponent.hero是HeroDetailComponent角度的输入属性,由于数据从模板绑定表达式流入该属性。
HeroDetailComponent.deleteRequest是从HeroDetailComponent角度来看的一个输出属性,由于在模板绑定语句中,事件流出该属性并处理该处理程序。
有时输入/输出属性的公共名称应与内部名称不一样。
属性指令一般是这种状况。指令消费者但愿绑定到指令的名称。 例如,当您使用myClick选择器将指令应用于<div>标记时,您但愿绑定到的事件属性也称为myClick。
<div (myClick)="clickMessage=$event" clickable>click with myClick</div>
可是,指令名称是指令类中属性名称一般来讲是一个糟糕的选择。 指令名不多描述属性的做用。 myClick指令名称对于发出点击消息的属性不是一个好名字。
幸运的是,您能够建立符合常规指望的属性的公共名称,同时在内部使用不一样的名称。 在上面的示例中,代码经过myClick别名绑定到指令本身的click属性。
要为属性名称指定别名,请将别名传递到输入/输出装饰器,以下所示:
final _onClick = new StreamController<String>(); // @Output(alias) propertyName = ... @Output('myClick') Stream<String> get clicks => _onClick.stream;
模板表达式语言使用Dart语法的一个子集,辅以几个特殊的运算符,用于特定的场景。接下来的部分将介绍这些操做符中的两个:管道和安全导航操做符。
在准备使用绑定以前,表达式的结果可能须要进行一些转换。 例如,您能够将数字显示为货币,强制文本为大写,或筛选列表并对其进行排序。
对于这些小型转换来讲,Angular 管道是一个很好的选择。 管道是简单的函数,它接受一个输入值并返回一个转换后的值。 使用管道运算符(|),它们很容易在模板表达式中应用:
<div>Title through uppercase pipe: {{title | uppercase}}</div>
管道运算符将左边表达式的结果传递给右边的管道函数。
您能够经过多个管道连接表达式:
<!-- Pipe chaining: convert title to uppercase, then to lowercase --> <div> Title through a pipe chain: {{title | uppercase | lowercase}} </div>
您也能够将参数应用于管道:
<!-- pipe with configuration argument => "February 25, 1970" --> <div>Birthdate: {{currentHero?.birthdate | date:'longDate'}}</div>
json管道能够帮助调试绑定:
<div>{{currentHero | json}}</div>
生成的输出以下所示:
{ "id": 0, "name": "Hercules", "emotion": "happy", "birthdate": "1970-02-25T08:00:00.000Z", "url": "http://www.imdb.com/title/tt0065832/", "rate": 325 }
Angular安全导航运算符(?.)与Dart条件成员访问运算符同样,是防止属性路径中的空值的便利方法。 在这里,若是currentHero为空,则防止视图呈现失败。
The current hero's name is {{currentHero?.name}}
当如下数据绑定title属性为null时会发生什么?
The title is {{title}}
视图仍然呈现,但显示的值是空白的; 你只看到“The title is”没有任何东西。 这是合理的行为。 至少该应用程序不会崩溃。
假设模板表达式涉及一个属性路径,就像在下一个例子中显示一个空的英雄的name同样。
The null hero's name is {{nullHero.name}}
Dart抛出异常,Angular也抛出异常:
EXCEPTION: The null object does not have a getter 'name'.
更糟的是,整个视图消失。
若是hero属性不能为空,这将是合理的行为。 若是它永远不能为空,但它是空的,这是一个应该被捕获和修复的编程错误。 抛出异常是正确的。
另外一方面,属性路径中空值时不时出现可能还好,特别是当数据如今为空,未来将返回数据。
在等待数据的时候,视图应该没有怨言地呈现,而null属性路径应该像title属性同样显示为空白。
不幸的是,当currentHero为空时,应用程序崩溃。
你能够用*ngIf来解决这个问题。
<!--No hero, div not displayed, no error --> <div *ngIf="nullHero != null">The null hero's name is {{nullHero.name}}</div>
这些方法有好处,但可能会是累赘,特别是若是属性路径很长。 想象一下,在诸如a.b.c.d这样的长属性路径中的某个地方防止空值。
Angular安全导航操做符(?.)是一种更为流畅和方便的方法来防止在属性路径中出现空。表达式在达到第一个空值时会被释放。 显示器是空白的,但应用程序保持滚动没有错误。
<!-- No hero, no problem! --> The null hero's name is {{nullHero?.name}}
它适用于很长的属性路径,如a?.b?.c?.d。
您已经完成了对模板语法的概览。 如今是时候把这些知识应用到你本身的组件和指令上。