详解Angualr 组件间通信

Angualr 组件间通信

约定: 遵循Angular官方的说法,下文中的AngularJS代指1.x版本,Angular代指Angular2及以后的升级版本。

采用Angular(或者任意MV*)的前端框架开发单页应用(SPA)时,我们都可能会遇见如下的场景:

A组件和B组件之前需要相互通信,或是A路由状态需要知道B路由状态的信息等等业务需求。

这个时候就需要设计到采用一套合理的通信方案来解决数据同步,数据通信的问题。

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编程思想, 她的学习收益比绝对让你想象不到。

RxJS不需要我们不断的手动去SharedService中检查数据是否产生了变更,而是在数据有变化时,主动将变动的数据推送给感兴趣的任何订阅者。

举个栗子:

我们有一份随时可能会发生变动的数据存在了服务A中,在没有使用RxJS(或是类似框架/库)的情况下,我们想要知道数据的变化, 我们可能会采用轮询的机制去不断的询问服务A,我们关心的数据是否发生了变化,如果变化了就做出相应的动作。我们处于一种盲目的主动状态。

更高级一点的实现,就是我们可能把服务A实现称为一个可被观察的对象,这样我们便能通过观察服务A的状态来获取数据的变动。

现在我们来使用RxJS实现,RxJS现在可以理解为更高级的观察者模式实现。当使用来RxJS之后,在服务A中的数据发生变化时,服务A会主动 将变动的数据推送给每一个感兴趣的‘消费者',这些消费者处于被动接受数据,自主处理数据的状态中。RxJS不同于普通观察者模式的地方在于, 它提供来一系列的数据操作方法,比如:filter, map等等。这样就便于我们更加精细的处理数据。

下面通过一段简单的示例代码来感受一下:

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的方式进行组件的封装,并将组件间的组织关系设计称为树状结构。这为应用数据的流向,管理提供了良好的支持, 也使得我们可以在Angular应用中使用一些别的库,比如: Redux 思想。基于这些设计理念,Angular为指令提供了更为强大的功能,组件也是指令。

采用 @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。

下面我们将展示如何使用这个这个组件,并为这个Input属性赋值。

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的指令中采用'@', ‘=', ‘&'等修饰的属性不一样的地方。

当父组件中的childName发生变化时,ChildComponent中的name属性并没有感知到变化。这是怎么了,是不是感觉新版的Angular在 和我们开玩笑,wtf!!!内心的表情是这样的 ○| ̄|_ 。(感觉一篇学习笔记开始被写的画风突变了。。。)

将父组件的属性变化映射到子组件中

上一小节的实现,虽然在初始化子组件时,我们可以将父组件的值反馈到子组件中。但是,初始化完成后,父组件中相关属性的变化却不能被子组件感知。

这无疑是让我们内心崩溃的。为什么和AngularJS不一样了???别急,下面我们将来提供解决方案。

利用Angular提供的组件生命周期钩子函数ngOnChanges来监听输入属性值的变化

需要实现让子组件感知到父组件中相关属性的变化,我们需要对Angualr组件的生命周期有一定的了解,采用Angular提供的组件生命周期的钩子函数, 进行组件间数据的同步。(关于Angualr组件的生命周期,之后会有相关的学习笔记整理。到时候在加上链接。)这里直接上代码:

import { Component, Input, SimpleChanges } from '@angular/core';

@Component({
 moduleId: module.id,
 selector: 'child-component',
 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方法, 当我们进行这一操作后,以后该对象上的相关属性的读取和赋值操作,都会调用相应的getter/setter方法进行预处理或改变操作结果。

同样的道理,在Angular中,我们通过设置和使用一个输入属性的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,
 selector: 'child-component',
 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, OnInit } from '@angular/core';
import { ChildComponent } from './child-component';

@Component({
 moduleId: module.id,
 selector: 'parent-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的生命周期钩子函数进行组件间通信。 我们还可以采用@ViewChild来进行不同组件间的通信,而不仅仅局限于父子组件间的通信。同时,采用@ViewChild的方式, 我们可以获得更为精细的组件控制权限,比如在父组件中读取子组件的属性值或调用子组件的方法。我们依然采用上面的代码来进行改造。

对于ChildComponent组件的变更:

import { Component } from '@angular/core';

@Component({
 moduleId: module.id,
 selector: 'child-component',
 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,
 selector: 'parent-component',
 // 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();
 }
}

通过上面的代码改造,我们同样可以实现不同组件间的通信,而且这样的组件通信已经不仅仅局限于父子组件间的通信了。

总结

由于技术水平和时间原因,这篇文章完成得比较粗略。主要整理的都是自己在工作中实际使用到的一些方案。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • AngualrJS中每次$http请求时的一个遮罩层Directive

    AngularJS是一款非常强大的前端MVC框架.在AngualrJS中使用$http每次向远程API发送请求,等待响应,这中间有些许的等待过程.如何优雅地处理这个等待过程呢? 如果我们在等待过程中弹出一个遮罩层,会是一个比较优雅的做法. 这就涉及到了对$http的请求响应进行拦截了.请求的时候,弹出一个遮罩层,收到响应的时候把遮罩层隐藏. 其实,$httpProvider已经为我们提供了一个$httpProvider.interceptors属性,我们只需要把自定义的拦截器放到该集合中就可以了

  • AngualrJS中的Directive制作一个菜单

    说下我经常写菜单的方式: <ul> <li data-ng-class="{'active': highlight('/orders')}"> <a href="#/orders">Orders</a> </li> </ul> 菜单项是否高亮显示取决于controller中的highlight方法. vm.highlight = funciton(path){ return $locaiton.

  • 详解Angualr 组件间通信

    Angualr 组件间通信 约定: 遵循Angular官方的说法,下文中的AngularJS代指1.x版本,Angular代指Angular2及以后的升级版本. 采用Angular(或者任意MV*)的前端框架开发单页应用(SPA)时,我们都可能会遇见如下的场景: A组件和B组件之前需要相互通信,或是A路由状态需要知道B路由状态的信息等等业务需求. 这个时候就需要设计到采用一套合理的通信方案来解决数据同步,数据通信的问题. AngularJS 组件间的数据通信 在AngularJS中,也就是Ang

  • 详解Vue组件之间通信的七种方式

    使用Vue也有很长一段时间,但是一直以来都没对其组件之间的通信做一个总结,这次就借此总结一下. 父子组件之间的通信 1)props和$emit 父组件通过props将数据下发给props,子组件通过$emit来触发自定义事件来通知父组件进行相应的操作 具体代码如下: ``` // 父组件 <template> <div> <h3>props和$emit</h3> <Children v-on:changeMsg="changeMsg"

  • 详解docker容器间通信的一种方法

    以我的ghost博客为例进行说明,我在VPS上用docker启动了两个ghost博客,还有一个Nginx做反向代理,将两个域名分别指向两个博客. docker启动命令 ghost: docker run -e NODE_ENV=production --name ghost1 -v /path/to/data/ghost/ghost1/:/var/lib/ghost -d ghost docker run -e NODE_ENV=production --name ghost2 -v /path

  • vue组件间通信子与父详解(二)

    接着vue组件父与子通信详解继续学习. 二.组件间通信(子组件传值给父组件) 通过事件的方式来完成数据的传输. ①在父组件中 定义一个方法,用来接收子组件所通过事件传来的值 methods:{ recvMsg:function(msg){ //参数msg就是子组件通过事件出来的数据 } } ②绑定事件处理函数 事件一般情况 都是自定义事件 <child-component @myEvent="recvMsg"></child-component> ③在子组件触发

  • 深入理解Android组件间通信机制对面向对象特性的影响详解

    组件的特点对于Android的四大组件Activity, Service, ContentProvider和Service,不能有Setter和Getter,也不能给组件添加接口.原因是组件都是给系统框架调用的,开发者只能实现其规定的回调接口,组件的创建与销毁都是由系统框架控制的,开发者不能强行干预,更没有办法获取组件的对象.比如Activity,Service,BroadcastReceiver,你没有办法去创建一个Activity,Service或BroadcastReceiver,然后像使

  • Vue常见组件间通信方案及典型应用场景详解

    目录 什么是组件通信 1.父子组件通信场景 2.兄弟组件通信场景 3.根组件和后代组件通信场景 4.插槽通信场景 5 无直接关系的组件通信场景 6 大型项目中的复杂组件通信场景 - Vuex状态管理 7 其他的一些组件通信方案 总结 什么是组件通信 所谓组件通信,就是组件之间的数据交互,也就是把一个组件A里面的数据传递到另一个组件B,并能够让组件B根据这个数据更新界面. 在 Vue中,可用的通信方案有很多,下面给大家描述几个常用的组件通信方案及其典型的应用场景. 1.父子组件通信场景 父子组件通

  • 详解Vue2中组件间通信的解决全方案

    前言 在Vue中组件是实现模块化开发的主要内容,而组件的通信更是vue数据驱动的灵魂,下面这篇文章将给大家介绍关于Vue2组件间通信的相关内容,下面话不多说,来一起看看详细的介绍. 组件通讯包括:父子组件间的通信和兄弟组件间的通信.在组件化系统构建中,组件间通信必不可少的. 父组件--> 子组件 1. 属性设置 父组件关键代码如下: <template> <Child :child-msg="msg"></Child> </templat

  • vue中组件通信详解(父子组件, 爷孙组件, 兄弟组件)

    vue中我们常常用到组件. 那么组件总体可以分为如下的几种关系. 父子组件, 爷孙组件, 兄弟组件. 这几种组件之间是如何通信的呢? 父子组件通信 根据vue中的文档可知, 组件的props属性用于接收父组件传递的信息. 而子组件想要向父组件传递信息, 可以使用$emit事件. 我们定义两个组件, 一个为父组件名为father, 另外一个为子组件child. 子组件通过props属性接收父组件传递的值, 这个值为fname, 是父组件的名字. 点击子组件的按钮, 触发toFather事件, 向父

  • Vue2.0学习之详解Vue 组件及父子组件通信

    什么是组件? vue中的组件其实就是页面组成的一部分,好比是电脑中的每一个元件(如硬盘,键盘,鼠标),它就是一个具有独立逻辑或界面,同时又能根据规定的接口规则进行相互融合,变成一个完整的应用. 页面就是由一个个类似这样的部分组成的,比如导航,列表,弹窗,下拉列表等.页面只不过是这些组件的容器,组件自由组合形成功能完整的界面,当不需要某个组件,或者想要替换某个组件时,可以随时进行替换和删除,而不影响整个应用的运行. 前端组件化的核心思路就是将一个巨大复杂的东西拆分成颗粒度合理的小东西. 使用组件的

  • 详解vue组件之间相互传值的方式

    概述 我们都知道 Vue 作为一个轻量级的前端框架,其核心就是组件化开发.Vue 就是由一个一个的组件构成的,组件化是它的精髓,也是最强大的功能之一.而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用. 但在实际项目开发过程中,我们需要访问其他组件的数据,这样就就有了组件通信的问题.在 vue 中组件之间的关系有:父子,兄弟,隔代.针对不同的关系,怎么实现数据传递,就是接下来要讲的. 一.父组件向子组件传值 即父组件通过属性的方式向子组件传值,子组件通过 props 来接收

随机推荐