Angular1组件通讯方式总结

这里需要将Angular1分为Angular1.5之前和Angular1.5两个不同的阶段来讲,两者虽然同属Angular1,但是在开发模式上还是有较大区别的。在Angular1.4及以前,主要是基于HTML的,将所有view划分为不同的HTML片段,通过路由,transclude,include等方式,按照用户行为切换显示不同界面。对于每个template内部来讲,可以是纯HTML,也可以是自定义的directive。directive之间可以有层级关系,也可以没有层级关系。在Angular1.5中,引入了Component API,鼓励使用单向数据流及Component tree 开发模式,在这种情况下,整个应用完全是基于组件的,除了root Component,其他Component都有自己的父级组件,组件之间主要是靠由上到下的单向数据流动来通信的,在这种模式下,directive不允许有自己的template,只能够来封装DOM操作。

在Angular1中提供了很多组件(指令)之间的通讯方式,本文主要来将这些方式一一列举出来,便于总结和使用。

directive通讯方式1-共享Service

在Angular1中,所有service都是单例的,这意味着一旦应用启动之后,在内存中会维护一些注册的service实例,在代码中任意地方对这些service的操作,都会导致其发生改变,而其他controller或者directive获取该service的值,也是发生改变之后的值。在这种情况下,一个directive对service进行写操作,另外一个directive对它进行读操作,两者就能通过该service进行通讯。

service定义

class HomeService {
  constructor() {
    this.names = [];
  }
  addName(name){
    this.names.push(name);
  }
}
10 export default HomeService;

directiveA定义

function aDirective(homeService) {
    "ngInject";
    return {
        restrict: ‘E‘,
        template: `name:<input type=‘text‘ ng-model=‘showValue‘>
  <br><button ng-click="addName()">addName</button>`,
        link: (scope, element, attrs) => {
            scope.addName = () => {
                if (scope.showValue) {
                    homeService.addName(scope.showValue);
                }
            }
        }
    };
}

export default aDirective;

directiveB定义

function bDirective(homeService) {
    "ngInject";
    return {
        restrict: ‘E‘,
        template: `<ul>
            <li ng-repeat="list in lists">{{list}}</li>
        </ul>`,
        link: (scope, element, attrs) => {
            scope.lists=homeService.names;
        }
    };
}

export default bDirective;

HTML

<main class="home">
  <h2>共享service实现Directive通信</h2>
  <div>
    <a-directive></a-directive>
    <b-directive></b-directive>
  </div>
</main>

在这里,我们在homeService中定义了一个addName方法,用来给该service的names中添加元素,然后让directiveA调用addName方法添加元素,随着names属性的变化,directiveB的list也会显示添加的内容,结果如下:

directive通讯方式2-自定义事件$broadcast及$emit

Angular1提供了scope之间事件的传播机制,使用scope.$emit可以往父级scope传播自定义事件并携带数据,使用scope.$broadcast可以给所有子scope广播事件和数据,所有需要接收到的scope需要使用scope.$on(eventName,callback)方法来监听事件。该机制很有用,但是我认为能不用就不用,主要是在实际开发中大多数是使用$rootScope来广播事件,每广播一次,整个scope下面的$$listeners都要去检测是否有对应的事件监听从而执行,如果scope层级较深,那么效率不会很高。除此之外,在各种directive,controller中监听自定义事件会导致混乱,代码不好去追踪,而且有可能引发命名冲突。

directiveA定义

function aDirective(homeService,$rootScope) {
    "ngInject";
    return {
        restrict: ‘E‘,
        template: `name:<input type=‘text‘ ng-model=‘showValue‘ class=‘about‘>
  <br><button ng-click="addName()">addName</button>`,
        link: (scope, element, attrs) => {
            scope.addName = () => {
                if(scope.showValue){
                    $rootScope.$broadcast(‘addName‘,scope.showValue);
                }
            }
        }
    };
}

export default aDirective;

directiveB定义

function bDirective(homeService) {
    "ngInject";
    return {
        restrict: ‘E‘,
        template: `<ul>
            <li ng-repeat="list in lists">{{list}}</li>
        </ul>`,
        link: (scope, element, attrs) => {
            scope.lists=[];
            scope.$on(‘addName‘,(...params)=>{
                scope.lists.push(params[1]);
            });
        }
    };
}

export default bDirective;

HTML

 <section>
2   <event-adirective class="about"></event-adirective>
3   <event-bdirective></event-bdirective>
4 </section>

在这里,DirectiveA使用rootScope来广播事件,directiveB来监听事件,然后将事件传递的参数添加到lists数组当中去,结果同上。

directive通讯方式3-在link函数中使用attrs通讯

每个directive在定义的时候都有一个link函数,函数签名的第三个参数是attrs,代表在该directive上面的所有atrributes数组,attrs提供了一些方法,比较有用的是$set和$observe,前者可以自定义attr或修改已经有的attr的值,后者可以监听到该值的变化。利用这种方式,我们可以让在位于同一个dom元素上的两个directive进行通讯,因为它们之间共享一个attrs数组。

directiveA定义

function aDirective($interval) {
    "ngInject";
    return {
        restrict: ‘A‘,
        link: (scope, element, attrs) => {
            let deInterval=$interval(()=>{
              attrs.$set(‘showValue‘, new Date().getTime());
            },1000);
            scope.$on(‘$destroy‘,()=>{
                $interval.cancel(deInterval);
            });
        }
    };
}

export default aDirective;

directiveB定义

function bDirective() {
    "ngInject";
    return {
        restrict: ‘E‘,
        template: `<span>{{time|date:‘yyyy-MM-dd HH:mm:ss‘}}</span>`,
        link: (scope, element, attrs) => {
            attrs.$observe(‘showValue‘, (newVal)=>{
                scope.time=newVal;
                console.log(newVal);
            });
        }
    };
}

export default bDirective;

HTML

1 <div>
2   <h2>{{ $ctrl.name }}</h2>
3   <directive-b directive-a></directive-b>
4 </div>

这里让directiveA不断修改showValue的值,让directiveB来observe该值并显示在template中,结果如下:

directive通讯方式4-使用directive的Controller+require来进行通讯

在directive中,可以在自己的controller中定义一些方法或属性,这些方法或者属性可以在其他directive中使用require来引入目标directive,然后在自己的link函数中的第四个参数中就可以拿到目标directive的实例,从而操作该实例,进行两者通讯。

directiveA定义

function aDirective() {
    "ngInject";
    return {
        restrict: ‘E‘,
        transclude: true,
        scope: {},
        template: `<div><div ng-transclude></div><ul>
            <li ng-repeat="list in lists">{{list}}</li>
        </ul></div>`,
        controller: function($scope) {
            "ngInject";
            $scope.lists  = [];
             this.addName = (item) => {
                    $scope.lists.push(item);
            }
        }
    };
}

export default aDirective;

directiveB定义

function bDirective() {
    "ngInject";
    return {
        restrict: ‘E‘,
        require:‘^^aCtrlDirective‘,
        template: `name:<input type=‘text‘ ng-model=‘showValue‘>
  <br><button ng-click="addName()">addName</button>
       `,
        link: (scope, element, attrs,ctrls) => {
            scope.addName=function(){
                if(scope.showValue){
                    ctrls.addName(scope.showValue);
                }
            }
        }
    };
}

export default bDirective;

HTML

<a-ctrl-directive>
  <div>
    <div>
      <b-ctrl-directive class=‘ctrls‘></b-ctrl-directive>
    </div>
    </div>
  </a-ctrl-directive>

在directiveA中定义自己的controller,暴露addName方法给外部,然后再directiveB中require引用directiveA,在directiveB的link函数中调用addName方法,从而操作directiveA的lists数组,lists并没有在directiveB中定义。

Component通讯方式-单向数据流+$onChanges hook方法

在Angular1.5之后,为了更好的升级到Angular2,引入了Component API,并鼓励使用单向数据流加组件树开发模式,这和之前的directive相比,开发模式发生了比较大的变化。虽然component本身仅仅是directive语法糖,但是其巨大意义在于让开发者脱离之前的HTML为核心,转而适应组件树开发方式,这种模式也是目前主流前端框架都鼓励使用的模式,如Angular2,React及Vue。在这种模式下,上述几种通讯方式仍然有效,但是并不是最佳实践,Component由于天生具有层级关系,所以更鼓励使用单向数据流+生命周期Hook方法来进行通讯。

这里我们模拟一个查询的场景,使用三个组件来完成该功能,最外层的一个searchBody组件,用来作为该功能的根组件,searchFiled组件用来接收用户输入,并提供查询按钮,searchList组件用来显示查询结果。

searchList定义(为了便于查看,我将该组件的HTMl及核心js一起展示)

import template from ‘./searchList.html‘;
import controller from ‘./searchList.controller‘;

let searchListComponent = {
  restrict: ‘E‘,
  bindings: {
    searchMessages:‘<‘,
    searchText:‘<‘
  },
  template,
  controller
};

class SearchListController {
    constructor() {
        this.name = ‘searchList‘;
    }
    $onInit() {
        this.initialMessages = angular.copy(this.searchMessages);
    }
    $onChanges(changesObj) {
        if (changesObj.searchText && changesObj.searchText.currentValue) {
            this.searchMessages = this.initialMessages.filter((message) => {
                return message.key.indexOf(this.searchText) !== -1;
            })
        }
    }

}

export default SearchListController;

<div>
  <ul class="search-ul">
    <li ng-repeat="item in $ctrl.searchMessages">
      {{item.key+"-"+item.val}}
    </li>
  </ul>
</div>

这里定义了一个controller,将searchList的所有逻辑都放在该controller中,6-9行在component定义中使用单向绑定<来定义来将其父组件上的数据绑定到controller上。18-20行在$onInit方法中初始化保留一份原始数据供查询使用。21-27行使用Angular1.5中Component的生命周期hook方法,当父组件中绑定的数据发生变化之后,都会触发该方法,该方法有一个参数,changesObj代表本次发生变化的对象,我们需要监听changesObj.searchText的变化,并按照searchText的最新值来过滤searchMessages.

searchField定义

import template from ‘./searchField.html‘;
import controller from ‘./searchField.controller‘;

let searchFieldComponent = {
  restrict: ‘E‘,
  bindings: {},
  template,
  controller,
  require:{
    searchBody:‘^searchBody‘
  }
};

class SearchFieldController {
  constructor() {
    this.searchWords = ‘‘;
  }

  doSearch(){
    if(!this.searchWords) return;
    this.searchBody.doSearch(this.searchWords);
  }

}

export default SearchFieldController;

<div>
  <input type="text" name="" value="" ng-model="$ctrl.searchWords">
  <button ng-click="$ctrl.doSearch()">search</button>
</div>

searchField的作用是使用input接受用户输入的查询参数,然后在点击button的时候调用searchBody的doSearch方法,来通知最外层的searchBody更新searchText。

searchBody定义

import template from ‘./searchBody.html‘;
import controller from ‘./searchBody.controller‘;

let searchBodyComponent = {
  restrict: ‘E‘,
  bindings: {},
  template,
  controller
};
class SearchBodyController {
  constructor() {
    this.searchTitle = ‘searchBody‘;
  }
  $onInit(){
    this.messages=[
      {key:"erer",val:"ererererererere"},
      {key:"1111",val:"111111111111111"},
      {key:"2222",val:"222222222222222"},
      {key:"3333",val:"333333333333333"},
      {key:"4444",val:"444444444444444"},
      {key:"5555",val:"555555555555555"},
      {key:"6666",val:"666666666666666"},
      {key:"7777",val:"777777777777777"},
      {key:"8888",val:"888888888888888"}
    ]
  }

  doSearch(text){
    this.searchText=text;
  }
}

export default SearchBodyController;

<div>
  <h1>{{ $ctrl.searchTitle }}</h1>
  <search-field></search-field>
  <search-list search-messages="$ctrl.messages" search-text="$ctrl.searchText"></search-list>
</div>

在上述代码中的37-38行,引用searchField和searchList两个组件,并将searchBody的messages及searchText作为最初的数据源传递给searchList组件,然后再searchField中点击查询按钮,会调用searchBody的doSearch方法,改变searchBody的searchText的值,然后触发searchList中的$onChanges方法,从而过滤相关结果,可以看到所有数据都是从上到下单向流动的,组件之间都是靠数据来通信的。

原文地址:https://www.cnblogs.com/aliwa/p/8490891.html

时间: 2024-11-15 00:22:05

Angular1组件通讯方式总结的相关文章

android ipc通信机制之之三,进程通讯方式。

IPC通讯方式的优缺点: IPC通讯方式的对比 名称 优点 缺点 适用场景 Bundle 简单易用 只能传输Bundle支持的数据类型 四大组件的进程通信 文件共享 简单易用 不适合高并发场景,并无法做到进程间即时通讯. 无并发访问情形,交换简单的数据是实时性不高的场景. AIDL 功能强大,支持一对多并发通信,支持实时通信. 使用稍微复杂,需要处理好线程同步. 一对多通信且有RPC需求 Messenger 功能一般,支持一对多串行通信,支持实时通信. 不能很好处理高并发情形,不支持RPC,数据

vue2.0中eventBus实现兄弟组件通讯

我们知道,在vue中父子组件的通讯是通过props和自定义事件搞定的,简单那的非父子组件通讯用bus(一个空的Vue实例),针对中大型的项目会选择vuex,然而小项目的话,便捷的解决方案就是eventBus. 官网相关描述在:$dispatch和$broadcast替换  一节中.提到: $dispatch 和 $broadcast 也没有解决兄弟组件间的通信问题.对于$dispatch 和 $broadcast最简单的升级方式就是:通过使用事件中心,允许组件自由交流,无论组件处于组件树的哪一层

Vue组件通讯

Vue最常用的组件通讯有三种:父->子组件通讯.子->父组件通讯,兄弟组件通讯.(template用的pug模板语法) 1.父->子组件通讯 父->子组件通讯,是通过props进行数据传递,并且具有这几个特性,单向传递,子组件接收的数据不可以更改,如果更改,会发出警告,每次父组件更新时,子组件的所有 prop 都会更新为最新值. 1 父组件 2 <template lang="pug"> 3 .father 4 Children(:name='msg

Vue最常用的组件通讯有三种:父-&gt;子组件通讯、子-&gt;父组件通讯,兄弟组件通讯.(template用的pug模板语法)

Vue最常用的组件通讯有三种:父->子组件通讯.子->父组件通讯,兄弟组件通讯.(template用的pug模板语法) 1.父->子组件通讯 父->子组件通讯,是通过props进行数据传递,并且具有这几个特性,单向传递,子组件接收的数据不可以更改,如果更改,会发出警告,每次父组件更新时,子组件的所有 prop 都会更新为最新值. 1 父组件 2 <template lang="pug"> 3 .father 4 Children(:name='msg

【工业串口和网络软件通讯平台(SuperIO)教程】九.重写通讯接口函数,实现特殊通讯方式

SuperIO相关资料下载:http://pan.baidu.com/s/1pJ7lZWf 1.1    统一的IO接口 开发一套设备驱动同时具备串口和网络通讯能力,通讯接口在逻辑上是统一的,在此基础上串口和网络也有自己的IO通讯特点,根据不同的通讯方式,可以把IIOChannel实例转换成ISessionSocket或ISessionCom实例.如下图: 1.2     通讯要求 一个请求命令分两次发送,每次发送数据时的串口校验位不同.先发送地址信息,这时串口的配置为Baud,m,8,1:再发

串口通讯方式1编程

在上位机上用串口调试助手发送一个字符X,单片机收到字符后返回给上位机"I get X",串口波特率设为9600bps. #include<reg52.h> #define uchar unsigned char unsigned char flag,a,i; uchar code table[]="I get"; void init() { TMOD=0x20;  //设定T1定时器的工作模式2 TH1=0xfd; //T1定时器装初值 TL1=0xfd

1.引入必要的文件 2.加载 UI 组件的方式 4.Parser 解析器

1 //引入 jQuery 核心库,这里采用的是 2.0 <scripttype="text/javascript"src="easyui/jquery.min.js"></script> //引入 jQuery EasyUI 核心库,这里采用的是 1.3.6 <scripttype="text/javascript"src="easyui/jquery.easyui.min.js"><

Linux 进程间通讯方式 pipe()函数 (转载)

转自:http://blog.csdn.net/ta893115871/article/details/7478779 Linux 进程间通讯方式有以下几种: 1->管道(pipe)和有名管道(fifo). 2->消息队列 3->共享内存 4->信号量 5->信号(signal) 6->套接字(sicket) 在这里我们看一下第一种====管道(pipe).有名管道(fifo)见其它文章. eg :我们以前学的命令 cat  file | grep  "abc

iOS开发-Socket通讯方式

1.程序之间的通信 两个应用程序之间的通信,我们可以理解为进程之间的通信,而进程之间进行通信的前提是我们能够找到某个进程,因此,我们需要给进程添加唯一的标示,在本地进程通信中我们可以使用PID来标示一个进程,但PID只在本地唯一,网络中的多个计算机之间的进程标示并不能保证唯一性,冲突的几率很大,这时候我们需要另辟蹊径,TCP/IP协议族已为我们解决了这个问题,IP层的ip地址可以标示主机,而TCP层协议和端口号可以标示某个主机的某个进程,于是我们采取"ip地址+协议+端口号"作为唯一标