学习如何编写显示数据并在数据绑定的帮助下使用用户事件的模板。
Angular应用程序管理用户看到和能够作的事情,经过组件类实例(组件)和面向用户的模板的交互来实现这一点。
您能够熟悉模型 - 视图 - 控制器(MVC)或模型 - 视图 - 视图模型(MVVM)的组件/模板。 在Angular中,组件扮演控制器/视图模型的一部分,模板表示视图。html
本指南涵盖了Angular模板语法的基本元素,以及构建视图所需的元素:java
现成示例(查看源代码)演示了本指南中描述的全部语法和代码片断。git
HTML是Angular模板的语言。 几乎全部的HTML语法都是有效的模板语法。 <script>元素是一个值得注意的例外。 这是被禁止的,消除脚本注入攻击的风险。 在实践中,<script>被忽略,并在浏览器控制台中出现警告。 有关详情,请参阅安全性页面。github
一些合法的HTML在模板中没有多大意义。 <html>,<body>和<base>元素没有用处。 剩下一切都是一致的。web
您可使用组件和指令出现的新元素和属性来扩展模板的HTML词汇表。 在下面的章节中,您将学习如何经过数据绑定来动态获取和设置DOM(文档对象模型)值。express
从数据绑定插值的第一种形式开始,看看有多少更丰富的模板HTML可使用。请回到顶部。api
在Angular的早期教程中,你遇到了插值的双曲括号{{and}}。浏览器
<p>My current hero is {{currentHero.name}}</p>
您可使用插值将计算的字符串组织到HTML元素标记和属性赋值之间的文本中。缓存
<h3> {{title}} <img src="{{heroImageUrl}}" style="height:30px"> </h3>
大括号里的文本一般是组件属性的名称。 Angular用相应的属性值替换该名称。 在上面的例子中,Angular评估了title和heroImageUrl属性,并“填充空白”,首先直接显示一个应用标题,而后是一个英雄图像。安全
更多的,大括号之间的文本是一个模板表达式,Angular首先评估并转换为一个字符串, 经过添加这两个数字来进行如下内插:
<!-- "The sum of 1 + 1 is 2" --> <p>The sum of 1 + 1 is {{1 + 1}}</p>
该表达式能够调用主机的方法,例如getVal(),以下所示:
<!-- "The sum of 1 + 1 is not 4" --> <p>The sum of 1 + 1 is not {{1 + 1 + getVal()}}</p>
Angular用双曲花括号评估全部表达式,将表达式结果转换为字符串,并将它们与相邻的文字串相连接。最后,它将这个复合插值结果赋值给一个元素或指令属性
您彷佛在元素标记之间插入结果并将其分配给属性。这么想很方便,你会由于这个错误而受苦。虽然这不彻底正确。插值是收敛到属性绑定中的一种特殊语法,以下所述。
但首先,让咱们仔细看看模板表达式和语句。
模板表达式产生一个值。 Angular执行表达式并将其分配给绑定目标的属性; 目标多是HTML元素,组件或指令。
{{1 + 1}}中的内插大括号包围模板表达式1 + 1.在下面的属性绑定部分中,在[property] =“expression”中,模板表达式显示在符号右侧的引号中。
你用相似Dart的语言编写这些模板表达式。 许多Dart表达式是合法的,但不是所有。
带有或促进反作用的Dart表达式是被禁止的,包括:
与Dart语法的其余显着差别包括:
不支持Dart字符串插值; 例如,而不是“'The title is $title'”,你必须写''The title is ' + title'“
不支持按位运算符| 和&
新的模板表达式运算符,如|
表达式上下文一般是组件实例。 在如下片断中,双花括号内的标题和引号中的isUnchanged引用了AppComponent的属性。
{{title}} <span [hidden]="isUnchanged">changed</span>
一个表达式也能够用来引用模板上下文的属性,包括模板输入变量(let hero)或模板引用变量(#heroInput)。
<div *ngFor="let hero of heroes">{{hero.name}}</div> <input #heroInput> {{heroInput.value}}
表达式中术语的上下文是模板变量和组件成员的混合。 若是引用这些名称空间的名称,则模板变量名称优先,后面是指令的上下文,最后是组件的成员名称。
前面的例子显示了这样一个名字冲突。 该组件具备hero属性,而* ngFor定义了英雄模板变量。 {{hero.name}}中的英雄是指变量输入变量,而不是组件的属性。
模板表达式不能引用静态属性,也不能引用顶层变量或函数,如来自dart:html的window 或document 。他们不能直接调用从dart:math导入的print或函数。 它们仅限于引用表达式上下文的成员。
模板表达式能够构建或破坏应用程序。 请遵循如下准则:
这些指导方针的例外状况应该是在你理解的状况下。
模板表达式不该该更改目标属性的值之外的任何应用程序状态。
这个规则对Angular的“单向数据流”策略是必不可少的。您没必要担忧读取组件值可能会改变一些其余的显示值。这个视图在整个渲染过程当中应该是稳定的。
Angular在每一个更改检测周期后执行模板表达式。 更改检测周期由许多异步活动触发,如承诺的分辨率,http结果,计时器事件,按键和鼠标移动。
表达式应该快速完成,不然用户可能会遇到卡帧,尤为是在较慢的设备上。 当他们的计算成本很高时,考虑缓存值。
虽然能够编写至关复杂的模板表达式,可是应该避免使用它们。
属性名称或方法调用应该是标准。 偶尔的布尔否认(!)能够。
另外, 将应用和业务逻辑放到到组件自己,在那里它将更容易开发和测试。
幂等表达式是理想的,由于它没有反作用,而且改善了Angular的变化检测性能。
对Angular来讲,一个幂等表达式老是返回彻底相同的东西,直到它的一个依赖值发生变化。
在事件循环的一个回合期间,依赖值不该该改变。若是一个幂等表达式返回一个字符串或一个数字,当它在一行中调用两次时会返回相同的字符串或数字。若是表达式返回一个对象(包括一个List),它将在连续调用两次时返回相同的对象引用。
模板语句响应绑定目标(例如元素,组件或指令)引起的事件。 您会在事件绑定部分看到模板语句,并在(event)=“statement”中出如今=符号右侧的引号中。
<button (click)="deleteHero()">Delete hero</button>
模板语句有一方面的做用。 它是一个事件的所有。 就是如何从用户操做更新应用程序状态。
响应事件是Angular的“单向数据流”的另外一面。在事件循环的这个周期中,您能够自由地在任何地方进行全部更改。
像模板表达式同样,模板语句使用了一种看起来像Dart的语言。 模板语句解析器与模板表达式解析器不一样,特别支持基本的赋值(=)和连接表达式(with;)
可是,某些Dart语法是不容许的:
与表达式同样,语句只能引用语句上下文中的内容,例如组件实例的事件处理方法。
语句上下文一般是组件实例。 (click)=“deleteHero()”中的deleteHero是数据绑定组件的一种方法。
<button (click)="deleteHero()">Delete hero</button>
语句上下文也能够引用模板本身的上下文的属性。 在如下示例中,将模板$ event对象,模板输入变量(let hero)和模板引用变量(#heroForm)传递给组件的事件处理方法。
<button (click)="onSave($event)">Save</button> <button *ngFor="let hero of heroes" (click)="deleteHero(hero)">{{hero.name}}</button> <form #heroForm (ngSubmit)="onSubmit(heroForm)"> ... </form>
模板上下文字段优先于组件上下文字段。 在上面的deleteHero(hero)中,hero是模板输入变量,而不是组件的hero属性。
模板语句不能引用类的静态属性,也不能引用顶层变量或函数,如来自dart:html的window或document 。 它们不能直接调用从dart:math导入的print或函数。
与表达式同样,避免编写复杂的模板语句。 方法调用或简单的属性分配应该是标准。
如今您已经感受到了模板表达式和语句,您已经准备好了解超越插值的各类数据绑定语法。
数据绑定是一种协调用户看到应用程序数据值的机制。 虽然您能够将值推送到HTML中,并从HTML中提取值,可是若是将这些琐事转换为绑定框架,则应用程序更易于编写,读取和维护。 您只需声明绑定源和目标HTML元素之间的绑定,而后让框架完成工做。
Angular提供了多种数据绑定。 本指南涵盖了大部分的Angular数据绑定及其语法的高级使用。
绑定类型能够按照数据流的方向分为三类:source-to-view,view-to-source,以及双向顺序:view-to-source-to-view:
数据方向 | 语法 | 类型 |
---|---|---|
单向 从数据源到目标视图 |
{{expression}} [target]="expression" bind-target="expression"
|
插值 组件属性 元素属性 元素类 元素样式 |
单向 从目标视图到数据源 |
(target)="statement" on-target="statement"
|
事件 |
双向 | [(target)]="expression" bindon-target="expression"
|
双向 |
除插值之外的绑定类型在等号左边或者用标点符号([],())包围,或者带前缀(bind-,on-,bindon-)都有一个目标名称。
目标名称是一个属性的名称。 它可能看起来像一个元素属性的名称,但它不是。 为了体会差别性,您必须开发一种思考HTML模板的新方法。
借助数据绑定的全部功能以及使用自定义标记扩展HTML词汇表的能力,将HTML模板视为HTML Plus是颇有诱惑力的
它确实是HTML Plus。 可是它也与你习惯的HTML有很大的不一样。 它须要一个新的心智模式。
在HTML开发的正常过程当中,您可使用HTML元素建立一个可视结构,并经过使用字符串常量设置元素属性来修改这些元素。
<div class="special">Mental Model</div> <img src="assets/images/hero.png"> <button disabled>Save</button>
您仍然在Angular模板中以这种方式建立结构并初始化属性值。
而后,您将学习如何使用封装了HTML的组件建立新元素,并将它们放入模板中,就好像它们是原生HTML元素同样。
<!-- Normal HTML --> <div class="special">Mental Model</div> <!-- Wow! A new element! --> <hero-detail></hero-detail>
这是HTML Plus。
而后你学习数据绑定。 你遇到的第一个绑定多是这样的:
<!-- Bind button disabled state to `isUnchanged` property --> <button [disabled]="isUnchanged">Save</button>
你的直觉可能代表你绑定了按钮的disabled属性,并将其值设置为组件的isUnchanged属性的当前值。 那个直觉是不正确的!
平常的HTML心智模式是误导性的。 一旦你开始数据绑定,你再也不使用HTML Attributes 。 你不是设置属性(Attributes) ; 你应该设置DOM元素,组件和指令的属性(Properties)。
HTML属性(Attributes)与DOM属性(Properties)
HTML属性和DOM属性的区别对于理解Angular绑定是如何工做是相当重要的。
Attributes 由HTML定义。Properties 由DOM(文档对象模型)定义。
- 一些HTML属性(Attributes)映射到属性(Properties)1:1, id是一个例子。
- 一些HTML属性(Attributes)没有相应的属性(Properties)。 colspan就是一个例子。
- 一些DOM属性(Properties)没有相应的属性(Attributes)。 textContent就是一个例子。
- 许多HTML属性(Attributes)彷佛映射到属性(Properties)...但不是以你想象的方式!
最后一个类别含义模糊的,除非你知道这个通常规则:
属性(Attributes)初始化DOM属性(Properties),而后完工。 属性(Properties)值能够会改变; 属性(Attributes)值不能。
例如,当浏览器呈现<input type =“text” value =“Bob”>时,它会建立一个对应的DOM节点,其值属性(Properties)已初始化为“Bob”。
当用户在输入框中输入“Sally”时,DOM元素值属性变为“Sally”。 可是,HTML value属性保持不变,当访问输入元素的该属性:input.getAttribute('value')返回“Bob”。
HTML属性(Attributes) value指定初始值; DOM value属性(Properties)是当前值。
disabled 属性(Attributes)是另外一个特殊的例子。 按钮的disabled 属性(Properties)默认为false,所以按钮已启用。 当您添加disabled属性(Attributes)时,它的存在会将按钮的disabled属性(Properties)初始化为true,所以该按钮被禁用。
添加和删除disabled属性(Attributes)将禁用和启用该按钮。
该属性(Attributes)的值是可有可无的,这就是为何您不能经过编写<button disabled =“false”> Still Disabled </ button>来启用按钮的缘由。设置按钮的disabled属性(Properties)(例如,使用Angular绑定)禁用或启用按钮。属性(Properties)的值很重要。
HTML属性(Attributes)和DOM属性(Properties)是不同的,即便它们具备相同的名称。
这个事实值得重复:模板绑定使用属性(properties)和事件(events)发挥做用,而不是属性(attributes)。
一个没有属性的世界
在Angular的世界中,属性(attributes)的惟一做用是初始化元素和指令状态。 当你写数据绑定时,你只处理目标对象的属性(properties)和事件(events)。 HTML属性(attributes)不起做用。
记住这个模型,继续阅读以了解绑定目标。
数据绑定的目标是DOM中的东西。根据绑定类型,目标能够是(element | component |directive)属性,(element | component | directive)事件或(不多)属性(attributes)名称。 下表总结了这些状况:
Type | Target | Example |
---|---|---|
Property | Element property Component property Directive property |
<img [src]="heroImageUrl"> <hero-detail [hero]="currentHero"></hero-detail> <div [ngClass]="{special: isSpecial}"></div>
|
Event | Element event Component event Directive event |
<button (click)="onSave()">Save</button> <hero-detail (deleteRequest)="deleteHero()"></hero-detail> <div (myClick)="clicked=$event" clickable>click me</div>
|
Two-way | Event and property | <input [(ngModel)]="name">
|
Attribute | Attribute (例外) | <button [attr.aria-label]="help">help</button>
|
Class | class property | <div [class.special]="isSpecial">Special</div>
|
Style | style property | <button [style.color]="isSpecial ? 'red' : 'green'">
|
您如今已经准备好详细查看绑定类型。
编写一个模板属性绑定来设置一个视图元素的属性。 该绑定将该属性设置为模板表达式的值。
最多见的属性绑定将元素属性设置为组件属性值。一个示例是将图像元素的src属性绑定到组件的heroImageUrl属性:
<img [src]="heroImageUrl">
另外一个例子是当组件标识isUnchanged的时候禁用一个按钮:
<button [disabled]="isUnchanged">Cancel is disabled</button>
另外一个是设置一个指令的属性:
<div [ngClass]="classes">[ngClass] binding to the classes property</div>
另外一个是设置自定义组件的模型属性(父组件和子组件进行通讯的一个好方法):
<hero-detail [hero]="currentHero"></hero-detail>
人们一般将属性绑定描述为单向数据绑定,由于它从一个组件的数据属性向一个目标元素属性传递一个值。
您不能使用属性绑定将值从目标元素中拉出。 您不能绑定到目标元素的属性来读取它。 你只能设置它。
一样,您不能在目标元素上使用属性绑定来调用方法。
若是元素引起事件,则可使用事件绑定来监听它们。
若是您必须读取目标元素属性或调用其中一个方法, 你须要一个不一样的技术。 查看ViewChild和ContentChild的API参考。
方括号之间的元素属性标识目标属性。 如下代码中的目标属性是图像元素的src属性。
<img [src]="heroImageUrl">
有些人更喜欢绑定前缀bind-替代,称为规范形式:
<img bind-src="heroImageUrl">
目标名称始终是属性(property)的名称,即便它看起来是别的名称。 你可能会看到src,并认为它是一个属性(attribute)的名称。 不是; 这是一个图像元素属性(property)的名称。
元素属性(property)多是更常见的目标,但Angular首先查看名称是不是已知指令的属性(property),以下例所示:
<div [ngClass]="classes">[ngClass] binding to the classes property</div>
从技术上讲,Angular将名称与指令输入或用@Input()装饰的属性相匹配。 这样的输入映射到指令本身的属性。
若是名称未能匹配已知指令或元素(property)的属性,则Angular会报告“未知指令”错误。
如前所述,模板表达式的评估必须没有可见的反作用。表达式语言自己是为了保证您的安全。您不能为属性绑定表达式中的任何东西赋值,也不能使用增量和减量运算符。
固然,该表达式可能会调用具备反作用的属性或方法。 Angular没法知道或阻止你。
该表达式能够调用相似getFoo()的东西。 只要你知道getFoo()是作什么的。若是getFoo()改变了某些东西,并且碰巧绑定了某些东西,你将冒着必定的风险。Angular可能会或可能不会显示更改的值。Angular可能会检测到更改并发出警告错误。一般来讲,保留数据属性和方法返回值就够了。
模板表达式应经过目标属性计算预期值的类型:
HeroDetail组件的hero属性须要一个Hero对象,这正是你在属性绑定中发送的内容:
<hero-detail [hero]="currentHero"></hero-detail>
检查模式异常
在检查模式下,若是模板表达结果类型和目标属性类型不是赋值兼容的,则会抛出一个类型异常。 有关检查模式的信息,请参阅Dart语言指南中的重要概念。
Dart 2.0注意:检查模式不会出如今飞镖2.0。 有关更多信息,请参阅Dart 2.0更新。
记住括号
括号告诉Angular评估模板表达式。 若是省略方括号,Angular会将该字符串视为常量,并使用该字符串初始化目标属性。 它不评估字符串!
不要犯如下错误:
<!-- ERROR: A value of type 'String' can't be assigned to a variable of type 'Hero'. <hero-detail hero="currentHero"></hero-detail> -->
检查模式类型异常例子
在检查模式下,上面的代码将致使一个类型异常:String不是Hero的子类型。
知足如下全部条件时,省略括号:
一次性字符串初始化在标准HTML中是常规的,而且它对于指令和组件属性也一样适用。 如下示例将HeroDetailComponent的prefix属性初始化为固定字符串,而不是模板表达式。 Angular设置它并再也不管它。
<hero-detail prefix="You are my" [hero]="currentHero"></hero-detail>
另外一方面,[hero]绑定仍然保留对组件的currentHero属性的有效绑定。
你常常有插值和属性绑定的选择。 如下绑定作一样的事情:
<p><img src="{{heroImageUrl}}"> is the <i>interpolated</i> image.</p> <p><img [src]="heroImageUrl"> is the <i>property bound</i> image.</p> <p><span>"{{title}}" is the <i>interpolated</i> title.</span></p> <p>"<span [innerHTML]="title"></span>" is the <i>property bound</i> title.</p>
在许多状况下插值是属性绑定较为方便的替代品。
将数据值呈现为字符串时,没有技术上的理由去选择另外一种形式,但插值更可读。咱们建议创建编码风格规则,选择符合规则的形式,对于手头的任务来讲是最天然的
将元素属性设置为非字符串数据值时,必须使用属性绑定。
想象下面的恶意内容。
String evilTitle = 'Template <script>alert("evil never sleeps")</script>Syntax';
幸运的是,Angular数据绑定对危险的HTML进行了警报。 它在显示它们以前清理这些值。 它不容许带脚本标记的HTML泄露到浏览器中,既不能使用插值也不能使用属性绑定。
<!-- Angular generates warnings for these two lines as it sanitizes them WARNING: sanitizing HTML stripped some content (see http://g.co/ng/security#xss). --> <p><span>"{{evilTitle}}" is the <i>interpolated</i> evil title.</span></p> <p>"<span [innerHTML]="evilTitle"></span>" is the <i>property bound</i> evil title.</p>
插值处理脚本标记与属性绑定不一样,但两种方法均无害地呈现内容。
模板语法为不太适合属性(property )绑定的场景提供了专门的单向绑定。
您能够直接使用属性绑定来设置属性的值。
这是绑定设置目标属性(property)的惟一例外规则。 这是建立和设置属性(attribute)的惟一一种绑定。
本指南反复强调,使用属性(property)绑定设置元素属性(property)始终优先于使用字符串设置属性(attribute)。 Angular为何提供属性(attribute)绑定?
当没有要绑定的元素属性时,必须使用属性绑定。
考虑ARIA,SVG和table span属性。 他们是纯粹的属性。 它们不对应元素属性,也不设置元素属性。 没有属性目标绑定。
在写像这样的东西时,这个事实变得很是明显:
<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属性。 它具备“colspan”属性(attribute),可是插值和属性(attribute)绑定只能设置属性(properties),而不能设置属性(attribute)。
您须要属性(attribute)绑定来建立和绑定到这些属性(attribute)。
属性(attribute)绑定语法相似于属性(properties)绑定。之前缀attr开头,后跟一个点(.)和属性名称代替括号之间的元素属性。而后使用解析为字符串的表达式来设置属性值。
将[attr.colspan]绑定到计算值:
<table border=1> <!-- expression calculates colspan=2 --> <tr><td [attr.colspan]="1 + 1">One-Two</td></tr> <!-- ERROR: There is no `colspan` property to set! <tr><td colspan="{{1 + 1}}">Three-Four</td></tr> --> <tr><td>Five</td><td>Six</td></tr> </table>
如下是表格的呈现方式:
属性(attribute)绑定的主要用例之一是设置ARIA属性,以下例所示:
<!-- create and set an aria attribute for assistive technology --> <button [attr.aria-label]="actionName">{{actionName}} with Aria</button>