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定义

  1. class HomeService {
  2. constructor() {
  3. this.names = [];
  4. }
  5. addName(name){
  6. this.names.push(name);
  7. }
  8. }
  9.  
  10. export default HomeService;

directiveA定义

  1. function aDirective(homeService) {
  2. "ngInject";
  3. return {
  4. restrict: 'E',
  5. template: `name:<input type='text' ng-model='showValue'>
  6. <br><button ng-click="addName()">addName</button>`,
  7. link: (scope, element, attrs) => {
  8. scope.addName = () => {
  9. if (scope.showValue) {
  10. homeService.addName(scope.showValue);
  11. }
  12. }
  13. }
  14. };
  15. }
  16.  
  17. export default aDirective;

directiveB定义

  1. function bDirective(homeService) {
  2. "ngInject";
  3. return {
  4. restrict: 'E',
  5. template: `<ul>
  6. <li ng-repeat="list in lists">{{list}}</li>
  7. </ul>`,
  8. link: (scope, element, attrs) => {
  9. scope.lists=homeService.names;
  10. }
  11. };
  12. }
  13.  
  14. export default bDirective;

HTML

  1. <main class="home">
  2. <h2>共享service实现Directive通信</h2>
  3. <div>
  4. <a-directive></a-directive>
  5. <b-directive></b-directive>
  6. </div>
  7. </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定义

  1. function aDirective(homeService,$rootScope) {
  2. "ngInject";
  3. return {
  4. restrict: 'E',
  5. template: `name:<input type='text' ng-model='showValue' class='about'>
  6. <br><button ng-click="addName()">addName</button>`,
  7. link: (scope, element, attrs) => {
  8. scope.addName = () => {
  9. if(scope.showValue){
  10. $rootScope.$broadcast('addName',scope.showValue);
  11. }
  12. }
  13. }
  14. };
  15. }
  16.  
  17. export default aDirective;

directiveB定义

  1. function bDirective(homeService) {
  2. "ngInject";
  3. return {
  4. restrict: 'E',
  5. template: `<ul>
  6. <li ng-repeat="list in lists">{{list}}</li>
  7. </ul>`,
  8. link: (scope, element, attrs) => {
  9. scope.lists=[];
  10. scope.$on('addName',(...params)=>{
  11. scope.lists.push(params[1]);
  12. });
  13. }
  14. };
  15. }
  16.  
  17. export default bDirective;

HTML

  1. <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定义

  1. function aDirective($interval) {
  2. "ngInject";
  3. return {
  4. restrict: 'A',
  5. link: (scope, element, attrs) => {
  6. let deInterval=$interval(()=>{
  7. attrs.$set('showValue', new Date().getTime());
  8. },1000);
  9. scope.$on('$destroy',()=>{
  10. $interval.cancel(deInterval);
  11. });
  12. }
  13. };
  14. }
  15.  
  16. export default aDirective;

directiveB定义

  1. function bDirective() {
  2. "ngInject";
  3. return {
  4. restrict: 'E',
  5. template: `<span>{{time|date:'yyyy-MM-dd HH:mm:ss'}}</span>`,
  6. link: (scope, element, attrs) => {
  7. attrs.$observe('showValue', (newVal)=>{
  8. scope.time=newVal;
  9. console.log(newVal);
  10. });
  11. }
  12. };
  13. }
  14.  
  15. 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定义

  1. function aDirective() {
  2. "ngInject";
  3. return {
  4. restrict: 'E',
  5. transclude: true,
  6. scope: {},
  7. template: `<div><div ng-transclude></div><ul>
  8. <li ng-repeat="list in lists">{{list}}</li>
  9. </ul></div>`,
  10. controller: function($scope) {
  11. "ngInject";
  12. $scope.lists = [];
  13. this.addName = (item) => {
  14. $scope.lists.push(item);
  15. }
  16. }
  17. };
  18. }
  19.  
  20. export default aDirective;

directiveB定义

  1. function bDirective() {
  2. "ngInject";
  3. return {
  4. restrict: 'E',
  5. require:'^^aCtrlDirective',
  6. template: `name:<input type='text' ng-model='showValue'>
  7. <br><button ng-click="addName()">addName</button>
  8. `,
  9. link: (scope, element, attrs,ctrls) => {
  10. scope.addName=function(){
  11. if(scope.showValue){
  12. ctrls.addName(scope.showValue);
  13. }
  14. }
  15. }
  16. };
  17. }
  18.  
  19. export default bDirective;

HTML

  1. <a-ctrl-directive>
  2. <div>
  3. <div>
  4. <b-ctrl-directive class='ctrls'></b-ctrl-directive>
  5. </div>
  6. </div>
  7. </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一起展示)

  1. import template from './searchList.html';
  2. import controller from './searchList.controller';
  3.  
  4. let searchListComponent = {
  5. restrict: 'E',
  6. bindings: {
  7. searchMessages:'<',
  8. searchText:'<'
  9. },
  10. template,
  11. controller
  12. };
  13.  
  14. class SearchListController {
  15. constructor() {
  16. this.name = 'searchList';
  17. }
  18. $onInit() {
  19. this.initialMessages = angular.copy(this.searchMessages);
  20. }
  21. $onChanges(changesObj) {
  22. if (changesObj.searchText && changesObj.searchText.currentValue) {
  23. this.searchMessages = this.initialMessages.filter((message) => {
  24. return message.key.indexOf(this.searchText) !== -1;
  25. })
  26. }
  27. }
  28.  
  29. }
  30.  
  31. export default SearchListController;
  32.  
  33. <div>
  34. <ul class="search-ul">
  35. <li ng-repeat="item in $ctrl.searchMessages">
  36. {{item.key+"-"+item.val}}
  37. </li>
  38. </ul>
  39. </div>

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

searchField定义

  1. import template from './searchField.html';
  2. import controller from './searchField.controller';
  3.  
  4. let searchFieldComponent = {
  5. restrict: 'E',
  6. bindings: {},
  7. template,
  8. controller,
  9. require:{
  10. searchBody:'^searchBody'
  11. }
  12. };
  13.  
  14. class SearchFieldController {
  15. constructor() {
  16. this.searchWords = '';
  17. }
  18.  
  19. doSearch(){
  20. if(!this.searchWords) return;
  21. this.searchBody.doSearch(this.searchWords);
  22. }
  23.  
  24. }
  25.  
  26. export default SearchFieldController;
  27.  
  28. <div>
  29. <input type="text" name="" value="" ng-model="$ctrl.searchWords">
  30. <button ng-click="$ctrl.doSearch()">search</button>
  31. </div>

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

searchBody定义

  1. import template from './searchBody.html';
  2. import controller from './searchBody.controller';
  3.  
  4. let searchBodyComponent = {
  5. restrict: 'E',
  6. bindings: {},
  7. template,
  8. controller
  9. };
  10. class SearchBodyController {
  11. constructor() {
  12. this.searchTitle = 'searchBody';
  13. }
  14. $onInit(){
  15. this.messages=[
  16. {key:"erer",val:"ererererererere"},
  17. {key:"1111",val:"111111111111111"},
  18. {key:"2222",val:"222222222222222"},
  19. {key:"3333",val:"333333333333333"},
  20. {key:"4444",val:"444444444444444"},
  21. {key:"5555",val:"555555555555555"},
  22. {key:"6666",val:"666666666666666"},
  23. {key:"7777",val:"777777777777777"},
  24. {key:"8888",val:"888888888888888"}
  25. ]
  26. }
  27.  
  28. doSearch(text){
  29. this.searchText=text;
  30. }
  31. }
  32.  
  33. export default SearchBodyController;
  34.  
  35. <div>
  36. <h1>{{ $ctrl.searchTitle }}</h1>
  37. <search-field></search-field>
  38. <search-list search-messages="$ctrl.messages" search-text="$ctrl.searchText"></search-list>
  39. </div>

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

Angular1.x组件通讯方式总结的更多相关文章

  1. vue 组件通讯方式到底有多少种 ?

    前置 做大小 vue 项目都离不开组件通讯, 自己也收藏了很多关于 vue 组件通讯的文章. 今天自己全部试了试, 并查了文档, 在这里总结一下并全部列出, 都是简单的例子. 如有错误欢迎指正. 温馨 ...

  2. Angular1组件通讯方式总结

    这里需要将Angular1分为Angular1.5之前和Angular1.5两个不同的阶段来讲,两者虽然同属Angular1,但是在开发模式上还是有较大区别的.在Angular1.4及以前,主要是基于 ...

  3. Angular 组件通讯方式

    (一)父子组件 输入/输出属性    关键词  Input,Output,EventEmitter. 父子组件信息信息,分为 (1)子组件向父组件传递 (2)父组件向子组件传递 (二)模版变量与 @V ...

  4. vue组件通讯之provide / inject

    什么是 provide / inject [传送门] vue的组件通讯方式我们熟知的有 props $emit bus vuex ,另外就是 provide/inject provide/inject ...

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

    IPC通讯方式的优缺点: IPC通讯方式的对比 名称 优点 缺点 适用场景 Bundle 简单易用 只能传输Bundle支持的数据类型 四大组件的进程通信 文件共享 简单易用 不适合高并发场景,并无法 ...

  6. Omi教程-组件通讯

    组件通讯 Omi框架组建间的通讯非常遍历灵活,因为有许多可选方案进行通讯: 通过在组件上声明 data-* 传递给子节点 通过在组件上声明 data 传递给子节点 父容器设置 childrenData ...

  7. Angular4 组件通讯方法大全

    组件通讯,意在不同的指令和组件之间共享信息.如何在两个多个组件之间共享信息呢. 最近在项目上,组件跟组件之间可能是父子关系,兄弟关系,爷孙关系都有.....我也找找了很多关于组件之间通讯的方法,不同的 ...

  8. 【Vue】Vue中的父子组件通讯以及使用sync同步父子组件数据

    前言: 之前写过一篇文章<在不同场景下Vue组件间的数据交流>,但现在来看,其中关于“父子组件通信”的介绍仍有诸多缺漏或者不当之处, 正好这几天学习了关于用sync修饰符做父子组件数据双向 ...

  9. Omi教程-组件通讯攻略大全

    组件通讯 Omi框架组建间的通讯非常遍历灵活,因为有许多可选方案进行通讯: 通过在组件上声明 data-* 传递给子节点 通过在组件上声明 data 传递给子节点 (支持复杂数据类型的映射) 父容器设 ...

随机推荐

  1. Android之获取string.xml文件里面的方法

    获取string.xml文件里面的方法 在此做个笔记: 1.在AndroidManifest.xml与layout等xml文件里: android:text="@string/resourc ...

  2. 获得省市 json 后台代码

    string connString = ConfigurationManager.ConnectionStrings["connStr"].ToString(); SqlConne ...

  3. [转]不正当使用HashMap导致cpu 100%的问题追究

    以前项目中遇到类似业务,但使用的是CurrentHashMap,看到这篇文章,转载记录,警示自己. 以下内容转自: 转载自并发编程网 – ifeve.com(http://ifeve.com/hash ...

  4. 使用Markdown+Pandoc+LaTex+Beamer制作幻灯片

    概述 为什么使用markdown? mardown是一种轻量级的标记语言,语法简单,可读性好,并且容易转化成其他格式的文档, 在技术文档撰写中得到越来越广泛的应用.相信大家对markdown都有一定了 ...

  5. WCF初探-4:WCF消息交换模式之请求与答复模式

    请求与答复模式( Request/Reply) 这种交换模式是使用最多的一中,它有如下特征: 调用服务方法后需要等待服务的消息返回,即便该方法返回 void 类型 相比Duplex来讲,这种模式强调的 ...

  6. Ubuntu 下载 & 编译 Android5.1 源码

    ustc & tsinghua android srchttps://lug.ustc.edu.cn/wiki/mirrors/help/aosphttps://mirrors.tuna.ts ...

  7. Grunt完成对LESS实时编译

    安装 安装grunt需要先安装node.js. 之后需要借助npm来安装grunt-cli,在cmd中npm install -g grunt-cli.(测试grunt --version看是否正确显 ...

  8. Android - 广播接收者 - BroadcastReceiver

    BroadcastReceiver 介绍: 广播是一种广泛运用的在应用程序之间传输信息的机制 .而 BroadcastReceiver 是对发送出来的广播 进行过滤接收并响应的一类组件 接受一种或者多 ...

  9. AngularJs的UI组件ui-Bootstrap分享(三)——Accordion

    Accordion手风琴控件使用uib-accordion和uib-accordion-group指令. <script> angular.module('myApp', ['ui.boo ...

  10. 批处理与python代码混合编程的实现方法

    批处理可以很方便地和其它各种语言混合编程,除了好玩,还有相当的实用价值, 比如windows版的ruby gem包管理器就是运用了批处理和ruby的混合编写, bathome出品的命令工具包管理器bc ...