模板语法

来源:互联网 发布:yishop 源码 编辑:程序博客网 时间:2024/05/16 01:21

一.HTML
几乎所有的HTML语法都是有效的,但是script元素被禁用了.以阻止脚本注入的风险

1.插值表达式
插值表达式可以把计算后的字符串插入到HTML元素标签内的文本或对标签的属性进行赋值.
一般来说,括号间的素材是一个模板表达式,Angular先对他求值,再把它转成字符串.
Angular对所有双括号的表达式进行求值,然后把求值的结果转换成字符串,并把他们跟相邻的字符串字面量连接起来,最后把合出来的插值结果赋给元素或指令的属性.
表面上来看,我们在元素标签之间插入了结果和对标签的属性进行赋值,但严格来说这是不对的,Angular把插值表达式转换成了属性绑定.

2.模板表达式
模板表达式产生一个值,Angular执行这个表达式,并把它赋值给绑定目标的属性,这个绑定目标可能是HTML元素,组件或者指令.
当我们写{{1 + 1}}时,是往插值表达式的括号中放进了一个模板表达式.
JS中那些可能具有副作用的表达式是被禁止的:
赋值运算符
new
使用;或者,的链式表达式
自增或者自减
不支持|和&

(1)表达式上下文
模板表达式不能使用全局命名空间的任何东西,不能引用window或者document.他们只局限于来自表达式上下文中的成员.
典型的表达式上下文就是这个组件实例.

(2)表达式指南

1)没有可见的副作用
模板表达式除了目标属性的值以外,不应该改变应用的任何状态

2)执行迅速
Angular执行模板比我们想象的更频繁,所以表达式应该快速结束,否则用户就会感到拖沓.

3)非常简单

4)幂等性
幂等性表示表达式应该返回完全相同的东西,直到他的某个依赖值发生改变

二.绑定语法

<!-- Bind button disabled state to `isUnchanged` property --><button [disabled]="isUnchanged">Save</button>

这是设置DOM元素而不是html的属性.

HTML的attribute和DOM的property的对比

attribute 是由 HTML 定义的。property 是由 DOM (Document Object Model) 定义的。

少量 HTML attribute 和 property 之间有着 1:1 的映射,如id。
A few HTML attributes have 1:1 mapping to properties. id is one example.
有些 HTML attribute 没有对应的 property,如colspan。
有些 DOM property 没有对应的 attribute,如textContent。
大量 HTML attribute看起来映射到了property…… 但却不像我们想的那样!

attribute初始化DOM,property的值可以改变,attribute的值不能改变.
例如浏览器渲染

<input type = "text" value="Bob">

其value property被初始化为property.当用户输入sally时,DOM元素的value property变成了sally,但是这个HTML value attribute保持不变.

disabled attribute是一个特别的例子,按钮的disabled property是false,因为默认情况下按钮是可用的,当我们添加disabled attribute时,只要他出现disabled property就初始化为true,于是按钮就被禁用了.
添加或删除disabled attribute会禁用或者启用这个按钮,但attribute的值无关紧要,这就是为什么我们不能通过disabled = “false”这种方法来启动按钮.
设置按钮的disabled property可以禁用或者启用按钮.

模板绑定是通过property和事件来工作的,而不是attribute.

二.属性绑定
当要把视图元素的属性设置为模板表达式时,就要写模板的属性绑定.
最常用的属性绑定是把元素属性设置为组件属性的值,下面这个例子,image元素的src属性会被绑定到组件的heroImageUrl属性上.

<img [src]="heroImageUrl">

还有个例子是设置自定义组件的模型属性(这是父子组件之间通讯的重要途径)

<hero-detail [hero]="currentHero"></hero-detail>

1.单向输入
人们经常把属性绑定描述成单向数据绑定,因为值得流动是单向的,从组件的数据属性流动到目标元素的属性.

不能使用属性绑定从目标元素上拉取值,也不能绑定到目标元素的属性来读取他.只能设置它.

也不能使用属性绑定来调用目标元素的方法,如果这个元素触发了事件,可以通过事件绑定来监听他们.

如果必须读取目标元素上的属性或者调用他的某个方法,可以使用另外一个方法,Viewchild和ContentChild.

2.绑定目标
包裹在方括号的元素属性名标记着目标属性,下列代码中的目标属性是src属性:

<img [src]="heroImageUrl">

元素属性可能是最常见的绑定目标,但Angular会先去看看这个名字是否是某个已知指令的属性名:

<div [ngClass]="classes">[ngClass] binding to the classes property</div>

严格来说,Angular正在匹配指令的输入属性的名字,这个名字是指令的inputs数组中所列的名字,或者是带有@Input()装饰器的属性,这些输入属性被映射为指令自己的属性.

1.返回恰当的类型

模板表达式应该返回目标属性所需要的类型的值.

2.属性绑定还是插值表达式
我们通常要在属性绑定还是插值表达式之间做出选择.下列这几对绑定做的事情完全相同:

<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>

在多数情况下,插值表达式是更好的选择,实际上,在渲染视图之前,Angular会把这些插值表达式翻译成相对应的属性绑定.

3.attribute绑定
可以通过attribute绑定来直接设置attribute的值.
attribute绑定语法与属性绑定类似,但方括号中的部分不是元素的属性名,而是由attr前缀,一个.和attribute的名字组成,可以通过值为字符串的表达式来设置attribute的值.
这里把[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 attribute(ARIA指可访问性,用于给残障人士访问互联网提供便利):

<!-- create and set an aria attribute for assistive technology --><button [attr.aria-label]="actionName">{{actionName}} with Aria</button>

4.css类绑定
借助css类绑定,可以从元素的class attribute上添加和移除css类名.
css类绑定语法和属性绑定类似,但方括号中的部分不是元素的属性名,而是由class前缀,一个.和css类的名字组成.

<!-- standard class attribute setting  --><div class="bad curly special">Bad curly special</div>

最后,可以绑定到特定的类名,当模板表达式的求值结果是真值时,Angular会添加他,反之会移除他.

<!-- 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>

5.样式绑定
通过样式绑定,可以设置内联样式
样式绑定的语法与属性绑定类似,由style前缀,一个.和css样式的属性名组成.

<button [style.color] = "isSpecial ? 'red': 'green'">Red</button><button [style.background-color]="canSave ? 'cyan': 'grey'" >Save</button>

6.事件绑定
事件绑定的语法是由等号左边带圆括号和右侧引号中的模板语句构成:

<button (click)="onSave()">Save</button>

7.&event和事件处理语句
在事件绑定中,Angular会为目标事件设置事件处理器.
在事件发生时,这个处理器会执行模板语句.典型的模板语句通常涉及到响应事件执行动作的接收器,例如从HTML控件取得值并存入模型.
绑定会通过名叫$event的事件对象传递关于此事件的消息(包括数据值).事件对象的形态取决于目标事件,如果目标事件的DOM元素事件,event就是DOM事件对象,他有像target和target.value这样的属性.
考虑这个范例:

<input [value]="currentHero.firstName"       (input)="currentHero.firstName=$event.target.value" >

上面的代码把输入框的value属性绑定到了firstName属性,要监听对值的修改,代码绑定到输入框的input事件.当用户更改输入时,input事件触发,并在包含DOM事件对象($event)的上下文中执行这条语句.

8.使用EvenEmitter实现自定义事件
通常,指令使用Angular EvenEmitter来触发自定义事件,指令创建一个EvenEmitter实例,并把它作为属性暴露出来.指令通过调用EvenEmitter.emit(payload)来触发事件,可以传入任何东西作为消息载荷.父指令通过绑定这个属性来监听事件,并通过$event对象来访问载荷.

假设HeroDetailComponent用于显示英雄的信息,并相应用户动作.虽然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);}

组件定义了deleteRequest属性,他是EvenEmitter实例,当用户点击删除时,组件会调用delete()方法,让EvenEmitter发出一个Hero对象.

现在假设有个宿主的父组件, 他绑定了HeroDetailComponent的deleteRequest事件.

<hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero"></hero-detail>

当deleteRequset事件触发时,Angular调用父组件的deleteHero方法,在$event变量中传入要删除的英雄(来自herodetail);

三.双向数据绑定
我们经常需要显示数据属性,并在用户做出更改时更新属性.
在元素层面上,既要设置元素属性,又要监听事件变化.
Angular为此提供了一种特殊的双向数据绑定语法,[(x)].

当一个元素拥有可设置的属性和对应的时间xchange时:

import { Component, EventEmitter, Input, Output } from '@angular/core';@Component({  selector: 'my-sizer',  template: `  <div>    <button (click)="dec()" title="smaller">-</button>    <button (click)="inc()" title="bigger">+</button>    <label [style.font-size.px]="size">FontSize: {{size}}px</label>  </div>`})export class SizerComponent {  @Input()  size: number | string;  @Output() sizeChange = new EventEmitter<number>();  dec() { this.resize(-1); }  inc() { this.resize(+1); }  resize(delta: number) {    this.size = Math.min(40, Math.max(8, +this.size + delta));    this.sizeChange.emit(this.size);  }}

size的初始值是一个输入值,来自属性绑定,点击按钮更改数值大小.

1.使用NgModel进行双向数据绑定

<input [(ngModel)]="currentHero.firstName">

下面展示了如何导入FormsModule,让NgModel变得可用

import { NgModule } from '@angular/core';import { BrowserModule }  from '@angular/platform-browser';import { FormsModule } from '@angular/forms';import { AppComponent } from './app.component';@NgModule({  imports: [    BrowserModule,    FormsModule  ],  declarations: [    AppComponent  ],  bootstrap: [ AppComponent ]})export class AppModule { }

ngModel数据属性设置元素的value属性,ngModelChange事件属性监听元素value的变化.
每种元素的特点各不相同,所以ngModel指令只能在一些特定的元素表单上使用,例如输入文本框,因为他们支持ControlValueAccessor.
除非写一个合适的值访问器,否则不能给自定义组件上.

四.内置指令

1.NgClass
我们经常用动态添加和删除CSS类的方式来控制元素如何显示,通过绑定到NgClass,可以同时添加和移除多个类.
绑定到一个key:value形式的控制对象,是应用NgClass的好方式,这个对象中的每一个key都是一个css类名,如果他的value是true,这个类就会被加上,否则就会被移除.

setClasses() {  let classes =  {    saveable: this.canSave,      // true    modified: !this.isUnchanged, // false    special: this.isSpecial,     // true  };  return classes;}

现在可以添加NgClass属性绑定,他会调用setClass,并相应的设置元素的类.

<div [ngClass]="setClasses()">This div is saveable and special</div>

2.NgStyle
我们可以通过组件的状态动态设置内联样式,NgStyle绑定可以同时设置多个内联样式.
NgStyle需要绑定到一个key:value对象,对象的每个key是样式名,对象的value是能用于这个样式的任何职.

setStyles() {  let styles = {    // CSS property names    'font-style':  this.canSave      ? 'italic' : 'normal',  // italic    'font-weight': !this.isUnchanged ? 'bold'   : 'normal',  // normal    'font-size':   this.isSpecial    ? '24px'   : '8px',     // 24px  };  return styles;}
<div [ngStyle]="setStyles()">  This div is italic, normal weight, and extra large (24px).</div>

3.NgIf
通过绑定NgIf指令到真值表达式,可以把元素子树添加到其DOM上.

<div *ngIf="currentHero">Hello, {{currentHero.firstName}}</div>

绑定到假值表达式将从DOM中移除元素子树.

<!-- because of the ngIf guard    `nullHero.firstName` never has a chance to fail --><div *ngIf="nullHero">Hello, {{nullHero.firstName}}</div><!-- Hero Detail is not in the DOM because isActive is false--><hero-detail *ngIf="isActive"></hero-detail>

可见性和NgIf是不同的.
我们可以通过类绑定或者样式绑定来显示和隐藏元素子树.

<!-- 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中物理的移除了这个元素子树,他销毁了子树中的组件及其状态,也潜在释放了可观的资源.

5.NgSwitch
当需要从一组可能的元素树中根据条件显示一个时,我们就把他绑定到NgSwitch中,Angular只把选中的元素树放进DOM中.

<span [ngSwitch]="toeChoice">  <span *ngSwitchCase="'Eenie'">Eenie</span>  <span *ngSwitchCase="'Meanie'">Meanie</span>  <span *ngSwitchCase="'Miney'">Miney</span>  <span *ngSwitchCase="'Moe'">Moe</span>  <span *ngSwitchDefault>other</span></span>

这里有三个相互合作的指令
1.ngSwitch
绑定到返回开关值的表达式

2.ngSwitchCase

3.ngSwitchDefault

不要再ngSwitch前面加星号,而应该使用属性绑定

6.ngFor
ngFor是一种重复器指令,自定义数据的一种方式
赋值给ngFor的字符串并不是模板表达式,而是一种微语法,由Angular自己解释的小型语言.

带索引的ngFor
ngFor指令支持可选的index,他在迭代过程中会从0增长到数组的长度,可以通过模板输入变量来捕获这个index,并在模板中使用.

<div *ngFor="let hero of heroes; let i=index">{{i + 1}} - {{hero.fullName}}</div>

ngFor指令有时候性能较差,特别在大型列表中,对一个条目的一丁点改动,移除或者添加,都会导致DOM级别的操作.
例如我们知道可以通过重新从服务器查询来刷新英雄列表,刷新后的列表可能包含很多以前显示过的英雄.
我们知道这一点,是因为英雄的id不会发生变化,但在Angular看来,他只是一个由新对象引用构成的新列表,他没有选择,只能清理旧列表,舍弃那些DOM元素,并且用新的DOM元素来重建一个列表.
如果有一个追踪函数,Angular就可以避免这种折腾,追踪函数告诉Angular,我们知道两个具有相同hero.id的对象其实是一种英雄:

trackByHeroes(index: number, hero: Hero) { return hero.id; }

现在把ngForTrackBy指令设置为那个追踪函数.

<div *ngFor="let hero of heroes; trackBy:trackByHeroes">({{hero.id}}) {{hero.fullName}}</div>

追踪函数不会阻止所有的DOM更改,如果同一个英雄的属性变化了,Angular就不得不更新DOM元素,但是这个属性没有变化,Angular就能留下这些DOM元素.

五.*与template
*是一种语法糖,他让那些需要借助模板来修改HTML布局的指令跟容易读写.ngFor,ngIf,ngSwitch都会添加或移除元素子树,这些元素子树被包裹在template中.
我们没有看到template这个标签,因为*前缀语法让我们忽略了这个标签,而把注意力集中到要包含要排除或者重复的那些HTML元素上.

六.模板引用变量
模板引用变量是模板中对DOM元素或指令的引用.
他能在原声DOM中使用,也能用于Angular组件.

1.引用模板引用变量
可以再同一元素,兄弟元素或者任何子元素中引用模板引用变量.
这里是关于创建和使用模板引用变量的例子:

<!-- phone refers to the input element; pass its `value` to an event handler --><input #phone placeholder="phone number"><button (click)="callPhone(phone.value)">Call</button><!-- fax refers to the input element; pass its `value` to an event handler --><input ref-fax placeholder="fax number"><button (click)="callFax(fax.value)">Fax</button>//也可以用ref-前缀

2.如何获取变量的值
Angular把这种变量的值设置为他所在的那个参数,在这个input元素上定义了这些变量,把那些input元素对象传给button元素,在事件绑定中,他作为参数传给了call方法.

3.ngForm和模板引用变量

<form (ngSubmit)="onSubmit(theForm)" #theForm="ngForm">  <div class="form-group">    <label for="name">Name</label>    <input class="form-control" name="name" required [(ngModel)]="currentHero.firstName">  </div>  <button type="submit" [disabled]="!theForm.form.valid">Submit</button></form>
<form (ngSubmit)="onSubmit(theForm)" #theForm="ngForm">  <button type="submit" [disabled]="!theForm.form.valid">Submit</button></form>

ngForm变量的值又是什么?
如果Angular没有接管他,那他可能只是一个HTMLFormElement,实际上他只是个Form,对Angular内置指令的ngForm的引用.

七.输入和输出属性
本节专注于绑定到的目标,他位于绑定声明的左侧,这些指令的属性必须被声明成输入或者输出.

我们重点突出下绑定目标和绑定源的区别:
绑定的目标是在=的左侧,目标源是在=的右侧
绑定的目标是绑定符[],{},或者[()]中的事件或者属性名,源则是引号”“中的部分或者是{[]}中的部分.
现在看看HeroComponent,他是绑定的目标:

<hero-detail [hero]="currentHero" (deleteRequest)="deleteHero($event)"></hero-detail>

1.声明输入属性和输出属性
目标属性必须被显式声明为输入或者输出

@Input()  hero: Hero;@Output() deleteRequest = new EventEmitter<Hero>();

另外,还可以在指令元数据中的inputs或者outputs数组中标记出这些成员.比如:

@Component({  inputs: ['hero'],  outputs: ['deleteRequest'],})

既可以通过装饰器,也可以通过元数据数组来指定输入输出属性,但不能同时用.

2.给输入/输出起别名
有时候需要让输入输出名字不同于内部名字.
把别名传给@Output或者@Input属性:

@Output('myClick') clicks = new EventEmitter<string>(); //  @Output(alias) propertyName = ...

也可以在数组中起别名:

@Directive({  outputs: ['clicks:myClick']  // propertyName:alias})

八.模板表达式操作符
1.安全导航操作符
Angular是一种便利的方式来保护出现在属性路径中的null和undefined值.当currentHero为空时, 保护视图渲染器防止他失败.

The current hero's name is {{currentHero?.firstName}}
0 0
原创粉丝点击