组件间的交互包括父子组件交互和一些非父子关系组件的交互。父子组件之间的交互通过常用的有三种方式:
1. 通过Angualr的输入属性(@Input)和输出属性(@Output)
通过输入型绑定把数据从父组件传到子组件(@Input):
以下面代码为例,父组件HeroParentComponent将获取到的hero数据通过属性绑定的方式流向子组件HeroChildComponent,子组件通过@Input装饰器进行数据接收。@Input装饰器告诉Angular,该属性是公共的,并且能被父组件绑定。 如果没有@Input,Angular就会拒绝绑定到该属性。这里重点关注父组件的[myHero]="hero"
,等号右侧hero,它属于模板所在的组件,不需要@Input装饰器。
等号左边[myHero],该属性属于其它组件或指令,它必须在子组件中带有@Input 装饰器。
父组件:
import { Component } from '@angular/core';
import { HEROES } from './hero';
@Component({
selector: 'hero-parent',
template: `
"let hero of heroes" [myHero]="hero">
hero-child>
`
})
export class HeroParentComponent {
heroes = HEROES;
}
子组件:
import { Component, Input } from '@angular/core';
import { Hero } from './hero';
@Component({
selector: 'hero-child',
template: `
{ {myHero.name} } says:h3>
<p>I, { {myHero.name} }, am at your service.p>
`
})
export class HeroChildComponent {
@Input() myHero: Hero;//通过@Input装饰器进行数据的接收
}
通过输出型绑定父组件监听子组件的事件(@Output):
使用事件传递是子组件向父组件传递数据最常用的方式。子组件需要实例化一个用来订阅和触发自定义事件的EventEmitter类,这个实例对象是一个由装饰器@Output修饰的输出属性。当用户期望操作行为发生时,可手动触发该事件,父组件则通过事件绑定的方式来订阅来自子组件触发的事件。
父组件:
import { Component } from '@angular/core';
@Component({
selector: 'vote-taker',
template: `
"let voter of voters" (onVote)="onVoted($event)">
my-voter>
`
})
export class VoteTakerComponent {
agreed = 0;
disagreed = 0;
voters = ['Mr. IQ', 'Ms. Universe', 'Bombasto'];
onVoted(agreed: boolean) {
agreed ? this.agreed++ : this.disagreed++;
}
}
子组件:
import { Component, EventEmitter, Output } from '@angular/core';
@Component({
selector: 'my-voter',
template: `
[disabled]="voted">Agree</button>
`
})
export class VoterComponent {
@Output() onVote = new EventEmitter();
voted = false;
vote(agreed: boolean) {
this.onVote.emit(agreed);//手动触发onVoted事件
this.voted = true;
}
}
如上代码,子组件VoterComponent点击按钮会触发vote事件,在子组件中通过emit属性触发这个自定义事件,并将传入参数agreed,父组件(onVote)="onVoted($event)"
,等号左边onVote是在子组件中通过@Output修饰器暴露出来的实例对象,等号右边是父组件的onVoted事件。父组件绑定的onVoted事件通过$event对象来访问传入的agreed数据。
2. 通过模板局部变量
模板引用变量通常用来引用模板中的某个DOM元素,它还可以引用Angular组件或指令或Web Component。使用井号 (#) 来声明引用变量。 可以在模板中的任何地方引用模板引用变量。 如示例,#phone
的意思就是声明一个名叫phone的变量来引用元素。声明在
上的phone变量就是在模板另一侧的
上使用的。大多数情况下,Angular会把模板引用变量的值设置为声明它的那个元素。 如示例中,phone引用的是表示电话号码的
框。 “拨号”按钮的点击事件处理器把这个input值传给了组件的callPhone方法。
<input #phone placeholder="phone number">
<button (click)="callPhone(phone.value)">Callbutton>
上面例子展示了一个简单的模板局部变量的使用,模板引用变量也可以用于父子组件通信。在父组件模板里,新建一个本地变量来代表子组件,然后利用这个变量来读取子组件的属性和调用子组件的方法。通过这个本地变量,父组件的模板就得到了子组件的引用,于是可以在父组件的模板中访问子组件的所有属性和方法。通过模板变量进行父子组件通讯有一定的局限性,就是模板变量只能在模板中使用,而不能直接在父组件类里使用。也就是说,父组件的类无法通过模板变量读取子组件的属性或调用子组件的方法。
3. 使用@ViewChild实现数据交互
当父组件类需要访问子组件的属性值或调用子组件的方法时,可以把子组件作为ViewChild,注入到父组件里面。下面示例中,父组件CountdownViewChildParentComponent通过@ViewChild属性装饰器,将子组件CountdownTimerComponent注入到私有属性timerComponent里面,然后在父组件自己的start方法中调用子组件的start方法。
父组件:
import { ViewChild } from '@angular/core';
import { Component } from '@angular/core';
import { CountdownTimerComponent } from './countdown-timer.component';
@Component({
selector: 'countdown-parent-vc',
template: `
`,
styleUrls: ['demo.css']
})
export class CountdownViewChildParentComponent implements AfterViewInit {
@ViewChild(CountdownTimerComponent)
private timerComponent: CountdownTimerComponent;
start() { this.timerComponent.start(); }//调用子组件的start方法
}
子组件:
import { Component} from '@angular/core';
@Component({
selector: 'countdown-timer',
template: '{{message}}
'
})
export class CountdownTimerComponent implements OnInit, OnDestroy {
message = '';
start() { this.countDown(); }
private countDown() {
//做一些事
}
}
从使用模型-视图-控制器 (MVC) 或模型-视图-视图模型 (MVVM) 的经验中,很多开发人员都熟悉了组件和模板这两个概念。 在 Angular 中,组件扮演着控制器或视图模型的角色,模板则扮演视图的角色。这里主要介绍模板语法中的数据绑定:
1. 插值表达式
数据绑定最常见的形式就是插值,使用的是双大括号{ { } }
的语法,插值表达式有几种常见用法:
<h3>
{ {title} }
<img src="{ {heroImageUrl} }" style="height:30px">
h3>
{ {title} }
这部分,形如title的值
,并最终在页面中展示出来。<h3>{ {title} }h3>
<p>The sum of 1 + 1 is not { {1 + 1 + getVal()} }p>
插值表达式可以理解为一个有带计算功能的的语法。
2. 属性绑定
数据绑定一种单向的数据绑定,数据从组件流向模板。当要把一个视图元素的属性设置为模板表达式时,就需要用到属性绑定。属性绑定大致可分为四类:
DOM元素属性绑定
HTML标签特性绑定
CSS类绑定
Style样式绑定
其实属性绑定和插值在本质上没有区别,在渲染视图之前,Angular会将插值表达式转换成属性绑定的形式,它只是属性绑定的一种语法糖。如下示例这两种写法是等价的:
<div>{{hello}}div>
<div [innerHTML]="hello">div>
3. 事件绑定
事件绑定也是一种单向数据绑定形式,数据从模板流向组件类。事件绑定的语法是由“=”左侧小括号内的目标事件和“=”右侧引号中的模板语句组成。在事件绑定中,Angular通过监听用户操作事件,比如键盘事件,鼠标事件等来执行其对应绑定的方法。
<a class="edit" (click)="editContact()">编辑a>
如上示例,用事件绑定来监听按钮的点击事件,只要触发点击事件,就会调用组件的editContact()方法。
4. 双向数据绑定
如上文所说,属性绑定实现了数据从组件类流向模板,事件绑定则实现了数据从模板流向组件类,将这两者结合起来,就实现了双向绑定的效果。双向绑定语法实际上是属性绑定和事件绑定的语法糖。使用Angular提供的NgModel指令可以方便地实现双向绑定:
<input [(ngModel)]="currentUser" neme="currentUser" />
如上代码,当组件类中的变量currentUser发生变化的时候,会使input的value值自动发生变化。而当input的value值发生变化时,组件类中的变量currentUser也会自动发生变化。需要注意的是:在ng2中使用ngModel必须带有name属性或者使用 [ngModelOptions]=”{standalone: true}”,如果未设置name或者ngModelOptions,就会报错。
上文为本人的笔记总结,如有错误,欢迎指正:)