Angualr 组件间通信
Angualr 组件间通信约定: 遵循Angular官方的说法,下文中的AngularJS代指1.x版本,Angular代指Angular2及以后的升级版本。 采用Angular(或者任意MV*)的前端框架开发单页应用(SPA)时,我们都可能会遇见如下的场景: AngularJS 组件间的数据通信在AngularJS中,也就是Angular JS 1.x版本中,我们需要实现控制器间的通信,有很多种方案,常见的有: 1. 采用 SharedService,利用共享的公共服务来实现数据交换。
AngularJS中的Service被设计成单例的,这为这一方案,提供来底层的实现可行性,这一方案也是被广泛采用的。
2. 利用AngularJS提供的事件机制,$rootScope.$broadcast/ $scope.$emit 配合 $on 方法实现。
该方案的实现具备一定的局限性,比如:$emit方法只能向上传递事件,而不能实现向下的事件传播。但是进行合理的搭配组合已经基本够用了。
3. 利用浏览器的SessionStorage或者LocalStorage进行数据交换。
由于浏览器提供的这两类本地存储方案都提供了相应的storage事件,所以我们也可以使用该方案进行数据交换。使用该方案是应该注意及时清理无关数据。
4. 采用响应式的编程思想或者观察者模式的应用。关于这一类实现,需要经历一个编程思想的转变,之后会通过专门的文章进行记录。
5. 自身实现共享变量池。这个难度比较大,没有一定的设计能力并不推荐。
由于AngularJS并不是本文的重点,所以这里只简单的提一下。后面介绍的Angular的方案也有许多可以通用的地方。 Angular 组件间的数据通信SharedService共享服务方案在新的Angular中依然可以使用,而且无需额外的学习成本。这里在之前的学习笔记里有记录,不再纪录了。 SharedService 搭配 RxJS听说 SharedService 和 RxJS 搭配更实用哦!这里的学习成本在于 RxJS,RxJS只是 Rx思想的JS实现。这里强烈推荐学习Rx编程思想, 下面通过一段简单的示例代码来感受一下: import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class DataService {
private data: any;
private subject: Subject<any> = new Subject<any>();
setData(data: any): void {
this.data = data;
this.subject.next(data);
}
getData(): Observable<any> {
return this.subject.asObservable();
}
}
上面的代码中,我们简单的实现了一个可观察的数据服务,下面我们来使用这个服务 import { Component,OnInit } from '@angular/core';
import { DataService } from './shared/Dataservice';
@Component({
moduleId: module.id,selector: '<my-component></my-component>',templateUrl: '.name.component.html',providers: [DataService]
})
export class MyComponent implements OnInit {
constructor(private dataService: DataService) {}
ngOnInit() {
// Will invoke data handler everytime
// when other component use the setter this.dataService.setData()
this.dataService.getData().subscribe(data => console.log(data));
}
}
使用Angular底层提供的 @Input 和 @Output 装饰器来实现组件间的通信服务新的Angular采用Web Component的方式进行组件的封装,并将组件间的组织关系设计称为树状结构。这为应用数据的流向,管理提供了良好的支持, 采用 @Input 修饰属性,实现 parent -> child 组件间的通信下面的示例代码将展示如何设置一个组件的Input属性 import { Component,Input,OnInit } from '@angular/core';
@Component({
moduleId: module.id,selector: 'child-component',template: `I'm {{ name }}`
})
export class ChildComponent implements OnInit {
@Input() name: string;
constructor() { }
ngOnInit() { }
}
上面的代码中,我们实现了一个名叫ChildComponent的组件,这个组件的有一个采用@Input装饰器修饰的属性:name。 import { Component,OnInit } from '@angular/core';
import { ChildComponent } from './child-component';
@Component({
moduleId: module.id,selector: 'parent-component',template: `<child-component [name]="childName"></child-component>`,// This is unnecessary when installing ChildComponent within Root NgModule
directives: [ChildComponent]
})
export class ParentComponent implements OnInit {
private childName: string;
constructor() { }
ngOnInit() {
this.childName = 'StevenShen';
}
}
上面的代码实现中,在父组件中,我们为子组件的Input属性设置了父组件中的特定值。关键点在如下片段: `<child-component [name]="childName"></child-component>`,
Angular在进行AOT操作时,会将特定的值注入给ChildComponent中。 如果你在CodePen,或是自己的本地实验上面的代码你会发现,和AngularJS的指令中采用’@’,‘=’,‘&’等修饰的属性不一样的地方。 将父组件的属性变化映射到子组件中上一小节的实现,虽然在初始化子组件时,我们可以将父组件的值反馈到子组件中。但是,初始化完成后,父组件中相关属性的变化却不能被子组件感知。 利用Angular提供的组件生命周期钩子函数ngOnChanges来监听输入属性值的变化需要实现让子组件感知到父组件中相关属性的变化,我们需要对Angualr组件的生命周期有一定的了解,采用Angular提供的组件生命周期的钩子函数, import { Component,SimpleChanges } from '@angular/core';
@Component({
moduleId: module.id,template: `I'm {{ name }}`
})
export class ChildComponent {
@Input() name: string;
ngOnChanges(changes: SimpleChanges) {
this.name = changes['childName'].currentValue;
}
}
采用ES5中的getter和setter方法进行输入属性的监听在ES5中,我们在定义一个对象的属性时,可以通过Object.defineProperty方法为一个对象的属性设置关联的getter和setter方法, 父组件的代码实现: import { Component } from '@angular/core';
@Component({
moduleId: module.id,selector: 'name-parent',template: ` <h2>Master controls {{names.length}} names</h2> <name-child *ngFor="let name of names" [name]="name"></name-child> `
})
export class ParentComponent {
names = ['StevenShen',' ',' lei '];
}
子组件的代码实现 import { Component,Input } from '@angular/core';
@Component({
moduleId: module.id,selector: 'name-child',template: `<h3>"{{name}}"</h3>`
})
export class ChildComponent {
name: string = 'default name';
@Input()
set name(name: string) {
this.name = (name && name.trim()) || 'default name';
}
get name() { return this.name; }
}
采用 @Output 修饰属性,实现 child -> parent 组件间的通信新版的 Angular 中,子组件和父组件间的通信,采用来事件的机制。这样的设计有助于组件的复用和代码的解耦; 直接上代码直观了解一下: @Component({
moduleId: module.id,template: `I'm {{ name }}`
})
export class ChildComponent {
@Input() name: string;
@Output() say: EventEmitter<boolean> = new EventEmitter<boolean>();
ngOnChanges(changes: SimpleChange) {
this.name = changes['childName'].currentValue;
}
speak() {
this.say.emit(true);
}
}
子组件变更完成后,我们来变更父组件的代码实现。 import { Component,template: `<child-component [name]="childName" (say)="isChildSpeak($event)"></child-component>`,// This is unnecessary when installing ChildComponent within Root NgModule
directives: [ChildComponent]
})
export class ParentComponent implements OnInit {
private childName: string;
constructor() { }
ngOnInit() {
this.childName = 'StevenShen';
}
isChildSpeak(isIt: boolean) {
console.log('The child speak status is %s',isIt ? 'ture' : 'false');
}
}
这样一来就实现了父子组件间的通信了。 通过 @ViewChild 获取组件的控制器/模版进行组件间的通信除开使用 @Input 和 @Output 修饰器搭配Angular的生命周期钩子函数进行组件间通信。 对于ChildComponent组件的变更: import { Component } from '@angular/core';
@Component({
moduleId: module.id,template: `I'm {{ name }}`
})
export class ChildComponent {
public name: string;
speak() {
console.log('say something whitout EventEmitter');
}
}
对于ParentComponent组件的变更: import { Component,OnInit,AfterViewInit,ViewChild,ElementRef } from '@angular/core';
import { ChildComponent } from './child-component.ts';
@Component({
moduleId: module.id,// attention #childCmp tag
template: `
<child-component #childCmp></child-component>
<button (click)="child.name = childName"></button>
`,// This is unnecessary when installing ChildComponent within Root NgModule
directives: [ ChildComponent ]
})
export class ParentComponent implements OnInit,AfterViewInit {
@ViewChild('childCmp') childCmp: ElementRef;
constructor() { }
ngOnInit() {
this.childCmp.name = 'StevenShen';
}
ngAfterViewInit() {
this.childCmp.speak();
}
}
通过上面的代码改造,我们同样可以实现不同组件间的通信,而且这样的组件通信已经不仅仅局限于父子组件间的通信了。 总结由于技术水平和时间原因,这篇文章完成得比较粗略。主要整理的都是自己在工作中实际使用到的一些方案。有不足之处或是更好的方案,希望大家在评论区指出。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |