[前端] Angular2+ 组件之间7种交互方式
一、通过@Input把数据从父组件传到子组件
示例:
UserParentComponent
import { Component } from '@angular/core';
import { USERS } from './user';
@Component({selector: 'app-user-parent',template: `{{master}} 有 {{users.length}} 个学生
`
})
export class UserParentComponent {users = USERS;master = '小张';
}
UserChildComponent
import { Component, Input } from '@angular/core';
import { User } from './user';
@Component({selector: 'app-user-child',template: `<p>{{user.name}} 的老师是 {{masterName}}.</p>`
})
export class UserChildComponent {@Input() user: User;@Input() masterName: string;
}
UserChildComponent组件有两个属性:user和masterName,它们都带有@Input装饰器,表示这两个属性都是输入型属性。
二、通过setter监听输入属性值的变化
示例:
@Input 装饰器也可以标注在setter方法上,以监听输入属性值的变化。
在本例中,子组件NameChildComponent的输入属性name上的这个setter方法会截掉名字里的空格,并把空字符串转换成默认字符串“未设置用户名”。
NameChildComponent
import { Component, Input } from '@angular/core';
@Component({selector: 'app-name-child',template: '"{{name}}"
'
})
export class NameChildComponent {private _name = '';@Input()set name(name: string) {this._name = (name && name.trim()) || '未设置用户名';}get name(): string { return this._name; }
}
NameParentComponent
import { Component } from '@angular/core';@Component({selector: 'app-name-parent',template: `<h2>{{master}} 有 {{users.length}} 个学生</h2><app-name-child *ngFor="let user of users"[name]="user"></app-name-child>`
})
export class NameParentComponent {// 显示 'Way Lau', '未设置名称', 'Bombasto', 'Magma'users = ['Way Lau', ' ', ' Bombasto ', ' Magma'];master = '小张';
}
程序运行效果
小张有4个学生
“Way Lau”
“未设置用户名”
“Bombasto”
“Magma”
三、通过ngOnChanges()方法监听输入属性值的变化
示例:
@Input 装饰器也可以标注在setter方法上,以监听输入属性值的变化。
VersionChildComponent
/* tslint:disable:forin */
import { Component, Input, OnChanges, SimpleChange } from '@angular/core';@Component({selector: 'app-version-child',template: `<h3>版本号 {{major}}.{{minor}}</h3><h4>更新日志:</h4><ul><li *ngFor="let change of changeLog">{{change}}</li></ul>`
})
export class VersionChildComponent implements OnChanges {@Input() major: number;@Input() minor: number;changeLog: string[] = [];ngOnChanges(changes: { [propKey: string]: SimpleChange }) {let log: string[] = [];for (let propName in changes) {let changedProp = changes[propName];let to = JSON.stringify(changedProp.currentValue);if (changedProp.isFirstChange()) {log.push(`初始化 ${propName} 设置为 ${to}`);} else {let from = JSON.stringify(changedProp.previousValue);log.push(`${propName} 从 ${from} 更改为 ${to}`);}}this.changeLog.push(log.join(', '));}
}
VersionParentComponent
import { Component } from '@angular/core';@Component({selector: 'app-version-parent',template: `<h2>版本号生成器</h2><button (click)="newMinor()">生成 minor 版本</button><button (click)="newMajor()">生成 major 版本</button><app-version-child [major]="major" [minor]="minor"></app-version-child>`
})
export class VersionParentComponent {major = 1;minor = 23;newMinor() {this.minor++;}newMajor() {this.major++;this.minor = 0;}
}
四、父组件监听子组件的事件
示例:
子组件暴露一个EventEmitter属性,当事件发生时,子组件利用该属性发出一个事件,父组件绑定到这个事件属性,就能在事件发生时做出回应。
子组件的EventEmitter属性是一个输出属性,通常带有@Output装饰器。
VoterComponent
import { Component, EventEmitter, Input, Output } from '@angular/core';@Component({selector: 'app-voter',template: `<h4>{{name}}</h4><button (click)="vote(true)" [disabled]="didVote">同意</button><button (click)="vote(false)" [disabled]="didVote">反对</button>`
})
export class VoterComponent {@Input() name: string;@Output() voted = new EventEmitter<boolean>();didVote = false;vote(agreed: boolean) {this.voted.emit(agreed);this.didVote = true;}
}
父组件VoteTakerComponent绑定了一个事件处理器onVoted(),用来响应子组件的事件并更新一个计数器。
import { Component } from '@angular/core';@Component({selector: 'app-vote-taker',template: `<h2>投票器</h2><h3>同意: {{agreed}}, 反对: {{disagreed}}</h3><app-voter *ngFor="let voter of voters"[name]="voter"(voted)="onVoted($event)"></app-voter>`
})
export class VoteTakerComponent {agreed = 0;disagreed = 0;voters = ['Way Lau', 'Bombasto', 'Magma'];onVoted(agreed: boolean) {agreed ? this.agreed++ : this.disagreed++;}
}
五、父组件与子组件通过本地变量交互
示例:
可以在父组件的模板里新建一个本地变量来代表子组件,然后利用这个变量来读取子组件的属性和调用子组件的方法。
CountdownTimerComponent
import { Component } from '@angular/core';@Component({selector: 'app-countdown-timer',template: '{{message}}
'
})
export class CountdownTimerComponent {intervalId = 0;message = '时间递减';seconds = 100;decrease() { this.countDown(); }private countDown() {this.seconds -= 1;}
}
父组件
CountdownLocalVarParentComponent
import { ViewChild } from '@angular/core';
import { Component } from '@angular/core';
import { CountdownTimerComponent } from './countdown-timer.component';// 父组件与子组件通过本地变量交互, #timer
@Component({selector: 'app-countdown-parent-lv',template: `<h3>时间递减(本地变量)</h3><button (click)="timer.decrease()">递减</button><div class="seconds">{{timer.seconds}}</div><app-countdown-timer #timer></app-countdown-timer>`,styleUrls: ['../assets/demo.css']
})
export class CountdownLocalVarParentComponent { }
父组件通过把本地变量放到标签中,用来代表子组件。这样,父组件的模板就得到了子组件的引用,就可以在父组件的模板中访问子组件的所有属性和方法。
六、父组件调用@ViewChild()方法获取子组件的值
示例:
如果父组件的类需要读取子组件的属性或调用子组件的方法,则不能使用本地变量方法。而应该把子组件作为ViewChild,注入父组件里来实现。
依然以上面事件递减例子为例,子组件CountdownTimerComponent保持不变。对父组件做一下调整。
CountdownViewChildParentComponent
// ViewChild
@Component({selector: 'app-countdown-parent-vc',template: `<h3>时间递减(ViewChild)</h3><button (click)="decrease()">递减</button><div class="seconds">{{ seconds() }}</div><app-countdown-timer></app-countdown-timer>`,styleUrls: ['../assets/demo.css']
})
export class CountdownViewChildParentComponent {@ViewChild(CountdownTimerComponent)private timerComponent: CountdownTimerComponent;decrease() { this.timerComponent.decrease(); }seconds() { return this.timerComponent.seconds; }
}
七、父组件和子组件通过服务来通信
示例:
父组件和它的子组件共享同一个服务,利用该服务来在内部实现双向通信。
mission.service.ts
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';@Injectable()
export class MissionService {// Observable string 源private missionAnnouncedSource = new Subject<string>();private missionConfirmedSource = new Subject<string>();// Observable string 流missionAnnounced$ = this.missionAnnouncedSource.asObservable();missionConfirmed$ = this.missionConfirmedSource.asObservable();announceMission(mission: string) {this.missionAnnouncedSource.next(mission);}confirmMission(astronaut: string) {this.missionConfirmedSource.next(astronaut);}
}
父组件:MissionControlComponent
import { Component } from '@angular/core';import { MissionService } from './mission.service';@Component({selector: 'app-mission-control',template: `<h2>导弹控制器</h2><button (click)="announce()">准备开始</button><app-astronaut *ngFor="let astronaut of astronauts"[astronaut]="astronaut"></app-astronaut><h3>日志</h3><ul><li *ngFor="let event of history">{{event}}</li></ul>`,providers: [MissionService]
})
export class MissionControlComponent {astronauts = ['操作员1', '操作员2', '操作员3'];history: string[] = [];missions = ['发射导弹'];nextMission = 0;constructor(private missionService: MissionService) {missionService.missionConfirmed$.subscribe(astronaut => {this.history.push(`${astronaut} 已经确认`);});}announce() {let mission = this.missions[this.nextMission++];this.missionService.announceMission(mission);this.history.push(`任务 "${mission}" 进入准备`);if (this.nextMission >= this.missions.length) { this.nextMission = 0; }}
}
子组件:AstronautComponent
import { Component, Input, OnDestroy } from '@angular/core';import { MissionService } from './mission.service';
import { Subscription } from 'rxjs';@Component({selector: 'app-astronaut',template: `{{astronaut}}: {{mission}}
`
})
export class AstronautComponent implements OnDestroy {@Input() astronaut: string;mission = '<没有任务>';confirmed = false;announced = false;subscription: Subscription;constructor(private missionService: MissionService) {this.subscription = missionService.missionAnnounced$.subscribe(mission => {this.mission = mission;this.announced = true;this.confirmed = false;});}confirm() {this.confirmed = true;this.missionService.confirmMission(this.astronaut);}ngOnDestroy() {// 防止内存泄漏this.subscription.unsubscribe();}
}
通过日志可以看到,父组件MissionControlComponent和子组件AstronautComponent之间,信息通过MissionService服务实现了双向传递。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
