显示数据
使用插值显示组件属性
选择模板来源
- 使用 @Component 装饰器的 template 属性来定义内联模板。内联模板对于小型示例或测试很有用。
@Component({
selector: 'app-root',
template: `
<h1>{{title}}</h1>
<h2>My favorite hero is: {{myHero.name}}</h2>
`
})
- 可以把模板定义在单独的 HTML 文件中,并且让 @Component 装饰器的 templateUrl 属性指向该文件。这种配置方式通常用于所有比小型测试或示例更复杂的场景中,它也是生成新组件时的默认值。
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
*ngFor 指令
template: `
<h1>{{title}}</h1>
<h2>My favorite hero is: {{myHero}}</h2>
<p>Heroes:</p>
<ul>
<li *ngFor="let hero of heroes">
{{ hero }}
</li>
</ul>
`
*ngIf 指令
template: `
<p *ngIf="heroes.length > 3">There are many heroes!</p>
`
Angular 的 ngIf 指令会根据一个布尔条件来显示或移除一个元素。
为数据创建一个类
模板语法
插值与模板表达式
插值
<p>{{title}}</p>
<div><img src="{{itemImageUrl}}"></div>
表达式
<img [src]="itemImageUrl2">
<ul>
<!-- 模板引用变量 -->
<li *ngFor="let customer of customers">{{customer.name}} </li>
</ul>
<label>Type something:
<!-- 模板引用变量 -->
<input #customerInput>{{customerInput.value}}
</label>
模板语句
模板语句出现在 = 号右侧的引号中,就像这样:(event)=“statement”。
<button (click)="deleteHero()">Delete hero</button>
<button (click)="deleteHeroXX('xx')">Delete xx</button>
<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) {
this.alert(`Delete ${hero ? hero.name : 'the hero'}.`);
}
deleteHeroXX(str) {
this.alert(str);
}
onSave(event?: MouseEvent) {
const evtMsg = event ? (event.target as HTMLElement).textContent : '';
this.alert(evtMsg);
if (event) { event.stopPropagation(); }
}
绑定语法:概览
Angular 提供了多种数据绑定方式,主要分为三类:
数据绑定与 HTML
数据绑定的通用规则:数据绑定使用 DOM 元素、组件和指令的 Property,而不是 HTML 的Attribute。
<!-- Bind button disabled state to `isUnchanged` property -->
<button [disabled]="isUnchanged">Save</button>
绑定类型与绑定目标
Property 绑定 [property]
单向输入
<img [src]="itemImageUrl">
<!-- Notice the colSpan property is camel case -->
<tr><td [colSpan]="2">Span 2 columns</td></tr>
<!-- Bind button disabled state to `isUnchanged` property -->
<button [disabled]="isUnchanged">Disabled Button</button>
<p [ngClass]="classes">[ngClass] binding to the classes property making this blue</p>
<app-item-detail [childItem]="parentItem"></app-item-detail>
属性绑定与插值
下列这几对绑定做的事情完全相同:
<p><img src="{{itemImageUrl}}"> is the <i>interpolated</i> image.</p>
<p><img [src]="itemImageUrl"> is the <i>property bound</i> image.</p>
<p><span>"{{interpolationTitle}}" is the <i>interpolated</i> title.</span></p>
<p>"<span [innerHTML]="propertyTitle"></span>" is the <i>property bound</i> title.</p>
attribute、class 和 style 绑定
attribute 绑定
Attribute 绑定的语法类似于 Property 绑定,但其括号之间不是元素的 Property,而是由前缀 attr、点( . )和 Attribute 名称组成。attribute 绑定的主要用例之一是设置 ARIA attribute(译注:ARIA 指无障碍功能,用于给残障人士访问互联网提供便利)。
<!-- create and set an aria attribute for assistive technology -->
<button [attr.aria-label]="actionName">{{actionName}} with Aria</button>
class 绑定
style 绑定
<button [style.color]="isSpecial ? 'red': 'green'">Red</button>
<button [style.background-color]="canSave ? 'cyan': 'grey'" >Save</button>
<button [style.font-size.em]="isSpecial ? 3 : 1" >Big</button>
<button [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button>
样式的优先级规则
- 某个类或样式绑定越具体,它的优先级就越高。
- 对具体类(例如 [class.foo] )的绑定优先于一般化的 [class] 绑定,对具体样式(例如 [style.bar] )的绑定优先于一般化的 [style] 绑定。
- 模板中的绑定是最具体的,因为它们直接并且唯一地应用于该元素,所以它们具有最高的优先级。
- 指令的宿主绑定被认为不太具体,因为指令可以在多个位置使用,所以它们的优先级低于模板绑定。
- 指令经常会增强组件的行为,所以组件的宿主绑定优先级最低。
- 绑定总是优先于静态属性。
class 和 [class] 具有相似的具体度,但 [class] 绑定优先,因为它是动态的。
<h3>Dynamic vs static</h3>
<!-- If `classExpr` has a value for the `special` class, this value will override the `class="special"` below -->
<div class="special" [class]="classExpr">Some text.</div>
<!-- If `styleExpr` has a value for the `color` property, this value will override the `style="color: blue"` below -->
<div style="color: blue" [style]="styleExpr">Some text.</div>
委托优先级较低的样式
更高优先级的样式可以使用 undefined 值“委托”给低级的优先级样式。虽然把 style 属性设置为 null 可以确保该样式被移除,但把它设置为 undefined 会导致 Angular 回退到该样式的次高优先级。
<comp-with-host-binding dirWithHostBinding></comp-with-host-binding>
dirWithHostBinding 指令和 comp-with-host-binding 组件都有 [style.width] 宿主绑定。在这种情况下,如果 dirWithHostBinding 把它的绑定设置为 undefined,则 width 属性将回退到 comp-with-host-binding 主机绑定的值。但是,如果 dirWithHostBinding 把它的绑定设置为 null,那么 width 属性就会被完全删除。
事件绑定 (event)
<!-- `myClick` is an event on the custom `ClickDirective` -->
<div>
<div (myClick)="clickMessage=$event" clickable>click with myClick</div>
{{clickMessage}}
</div>
,myClick是自定义指令,后续再讲!
<!-- 注意deleteHero($event)方法中的$event接收了子组件通过EventEmitter暴露的参数 -->
<app-hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero"></app-hero-detail>
deleteHero(hero?: Hero) {
this.alert(`Delete ${hero ? hero.name : 'the hero'}.`);
}
// hero-detail子组件
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Hero } from './hero';
@Component({
selector: 'app-hero-detail',
// tslint:disable: no-inputs-metadata-property no-outputs-metadata-property
inputs: ['hero'],
outputs: ['deleteRequest'],
// tslint:enable: no-inputs-metadata-property no-outputs-metadata-property
styles: ['button {margin-left: 8px} div {margin: 8px 0} img {height:24px}'],
template: `
<div>
<img src="{{heroImageUrl}}">
<span [style.text-decoration]="lineThrough">
{{prefix}} {{hero?.name}}
</span>
<button (click)="delete()">Delete</button>
</div>`
})
export class HeroDetailComponent {
hero: Hero = new Hero(-1, '', 'Zzzzzzzz'); // default sleeping hero
// heroImageUrl = 'http://www.wpclipart.com/cartoon/people/hero/hero_silhoutte_T.png';
// Public Domain terms of use: http://www.wpclipart.com/terms.html
heroImageUrl = 'assets/images/hero.png';
lineThrough = '';
@Input() prefix = '';
// This component makes a request but it can't actually delete a hero.
deleteRequest = new EventEmitter<Hero>();
delete() {
this.deleteRequest.emit(this.hero);
this.lineThrough = this.lineThrough ? '' : 'line-through';
}
}
$event 和事件处理语句
<div class="parent-div" (click)="onClickMe($event)" clickable>Click me
<div class="child-div">Click me too!</div>
</div>
onClickMe(event?: MouseEvent) {
const evtMsg = event ? ' Event target class is ' + (event.target as HTMLElement).className : '';
this.alert('Click me.' + evtMsg);
}
点击外层div:
点击内层div:
绑定会通过名叫 $event 的事件对象传递关于此事件的信息(包括数据值)。事件对象的形态取决于目标事件。如果目标事件是原生 DOM 元素事件, $event 就是 DOM 事件对象,它有像 target 和 target.value 这样的属性。
<!-- Will save only once -->
<div (click)="onSave()" clickable>
<button (click)="onSave($event)">Save, no propagation</button>
</div>
<!-- Will save twice -->
<div (click)="onSave()" clickable>
<button (click)="onSave()">Save w/ propagation</button>
</div>
onSave(event?: MouseEvent) {
const evtMsg = event ? (event.target as HTMLElement).textContent : '';
this.alert(evtMsg);
if (event) { event.stopPropagation(); }
}
如果拿到的是MouseEvent对象,可以阻止事件冒泡。
使用 EventEmitter 实现自定义事件
双向绑定 [(…)]
<div id="two-way-1">
<app-sizer [(size)]="fontSizePx"></app-sizer>
<div [style.font-size.px]="fontSizePx">Resizable Text</div>
<!-- [(ngModel)]需要结合FormsModule一起使用 -->
<label>FontSize (px): <input [(ngModel)]="fontSizePx"></label>
</div>
双向绑定会做两件事:
- 设置特定的元素属性。
- 监听元素的变更事件。
双向绑定语法实际上是属性绑定和事件绑定的语法糖。以上示例可以拆分成:
内置指令
内置属性型指令
最常见的属性型指令:
- NgClass —— 添加和删除一组 CSS 类。
- NgStyle —— 添加和删除一组 HTML 样式。
- NgModel —— 将数据双向绑定添加到 HTML 表单元素。
NgClass
<div [ngClass]="isSpecial ? 'special' : ''">This div is special</div>
<div [ngClass]="currentClasses">This div is initially saveable, unchanged, and special.</div>
this.currentClasses = {
'saveable': this.canSave,
'modified': !this.isUnchanged,
'special': this.isSpecial
};
NgStyle
<div [ngStyle]="currentStyles">
This div is initially italic, normal weight, and extra large (24px).
</div>
this.currentStyles = {
'font-style': this.canSave ? 'italic' : 'normal',
'font-weight': !this.isUnchanged ? 'bold' : 'normal',
'font-size': this.isSpecial ? '24px' : '12px'
};
[(ngModel)] :双向绑定
导入 FormsModule 以使用 ngModel
<input [value]="currentHero.name"
(input)="updateCurrentHeroName($event)">
without NgModel
<br>
<input [(ngModel)]="currentHero.name">
[(ngModel)]
<br>
<input bindon-ngModel="currentHero.name">
bindon-ngModel
<br>
<input
[ngModel]="currentHero.name"
(ngModelChange)="currentHero.name=$event">
(ngModelChange)="...name=$event"
<br>
<input
[ngModel]="currentHero.name"
(ngModelChange)="setUppercaseName($event)">
(ngModelChange)="setUppercaseName($event)"
setUppercaseName(name: string) {
this.currentHero.name = name.toUpperCase();
}
内置结构型指令
- NgIf —— 从模板中创建或销毁子视图。
- NgFor —— 为列表中的每个条目重复渲染一个节点。
- NgSwitch —— 一组在备用视图之间切换的指令。
NgIf 与Vue中的v-if指令相似
<app-item-detail *ngIf="isActive" [item]="item"></app-item-detail>
NgFor与Vue中的v-for指令相似
<div *ngFor="let item of items; let i=index">{{i + 1}} - {{item.name}}</div>
带 trackBy 的 *ngFor
<button (click)="changeIds()">Change ids</button>
<div *ngFor="let item of items; trackBy: trackByItems">
({{item.id}}) {{item.name}}
</div>
trackByItems(index: number, item: Item): number {
return item.id;
}
changeIds() {
this.items.forEach(i => {
if(i.id % 2 == 0) {
i.id += 2;
}
});
}
使用 trackBy,设置需要Angular跟踪的值。这个例子中,该值是英雄的 id,则只有修改了 id 的按钮才会触发元素替换。
NgSwitch 指令
NgSwitch 根据切换条件显示几个可能的元素中的一个。Angular 只会将选定的元素放入 DOM。
NgSwitch 实际上是三个协作指令的集合: ngSwitch,*ngSwitchCase和 *ngSwitchDefault,如以下示例所示。
<div [ngSwitch]="currentItem.feature">
<app-stout-item *ngSwitchCase="'stout'" [item]="currentItem"></app-stout-item>
<app-device-item *ngSwitchCase="'slim'" [item]="currentItem"></app-device-item>
<app-lost-item *ngSwitchCase="'vintage'" [item]="currentItem"></app-lost-item>
<app-best-item *ngSwitchCase="'bright'" [item]="currentItem"></app-best-item>
<app-unknown-item *ngSwitchDefault [item]="currentItem"></app-unknown-item>
</div>
绑定到 [ngSwitch]。如果试图写成 *ngSwitch,就会出现错误,因为 NgSwitch 是属性型指令,而不是结构型指令。它不会直接接触 DOM,而是会更改与之相伴的指令的行为。
绑定到 *ngSwitchCase 和 *ngSwitchDefault NgSwitchCase 和 NgSwitchDefault 指令都是结构型指令,因为它们会从 DOM 中添加或移除元素。
-
当 NgSwitchCase 的绑定值等于开关值时,就将其元素添加到 DOM 中;否则从 DOM 中删除。
-
NgSwitchDefault 会在没有任何一个 NgSwitchCase 被选中时把它所在的元素加入 DOM 中。