老规矩,先上效果图:
首先说一下本文的重点:
1、父指令如何调用子指令中的方法;2、子指令如何调用父指令中的方法;(都不是通过广播的方式)
如果有同学还不知道父指令如何通过=@&给子指令传递参数,或者一些其他自定义指令的属性(scope、complie、link……)的话,请查阅官网文档发或者其他文档博客。
前言:
在自学了一段时间VUE之后,被VUE简洁的代码以及强大的解耦能力深深折服,所有的界面元素都是组件,组件有聪明组件和笨组件之分,聪明组件就是业务组件,负责复杂的业务逻辑,而笨组件则负责一些基本的数据获取功能,界面显示等等,比如日期选择插件、轮播图显示等等,在开发的时候往往需要大量的笨组件。无奈公司标准产品的开发使用的是angularJs,一下子就有一种一夜回到解放前的感觉,这就跟从IntelliJIDEA回到eclipse的感觉是一样一样的。由于自学了一段时间VUE,搞的我现在使用angularJs也是使用VUE的思想,想方设法拜托angularJs中凌乱的代码,统统用指令解耦。
在angularJs中编写自定义指令没有VUE中编写组件那么方便,父子组件之间的通信,作用域的互相引用等等,在angularJs中并没有VUE中那么清晰,所以在编写自定义指令的时候需要特别注意一些问题。
这个demo是基于onsenui写的,主要用了了里面的ons-carousel轮播插件以及ons-icon图片两个自定义指令,不过这个对本次总结没有任何影响。
首先说一下界面结构,这个界面中有三个指令,第一个是page-tab,就是标题状态栏下面可以左右滑动的page1、2、3……的那一行,然后是下面白色区域的页面page-content,最后是page-tabbar,page-tabbar包含了page-tab以及page-content,首先看一下主页面的代码:
<ons-page class="hg-agency-item" ng-controller="hgAgencyItemController"> <ons-toolbar> <div class="left"> <ons-back-button></ons-back-button> </div> <div class="center"> <span ng-bind="'这个是标题'"></span> </div> <div class="right"> <ons-toolbar-button> <ons-icon icon="ion-search"></ons-icon> </ons-toolbar-button> </div> </ons-toolbar> <ons-page class="c-p"> <page-tabbar data="tabItems" tab-num="4"></page-tabbar> </ons-page> </ons-page>
很简单,就是在内容界面直接引用了page-tabbar指令,传入两个参数,data是数据,tab-num是page-tab显示的tab的个数,然后看一下controller:
由于部分数据不方便显示,所以打个码:
name就是tab的显示值,icon就是图标。数据很简单,用起来很简单,这就是编写自定义指令的目的。
name就是tab的显示值,icon就是图标。数据很简单,用起来很简单,这就是编写自定义指令的目的。
首先来看page-tabbar的HTML代码:
<ons-page> <page-tab data="data" item-num="{{tabNum}}" on-click="pageContent.setActiveIndex(index)" var="pageTab"></page-tab> <page-content data="data" var="pageContent" postchange="pageTab.setActiveIndex(index)"></page-content> </ons-page>
也是很简单:
app.directive('pageTabbar',function () { return { restrict: 'E', replace: false, templateUrl: 'js/directives/page-tabbar/page.tabbar.template.html', scope: { data: '=data', tabNum:'@tabNum' }, controller: [ '$scope','$element','$attrs','$transclude', function ($scope,$element,$attrs,$transclude) { angular.element(document).ready(function () { console.log('pageTabbar ready'); }); } ], link: function (scope,element,attrs) { } } });
这个指令的作用只是负责传递数据以及,当page-tab或者page-content改变时,通知另一个改变(调用指令中暴露的方法),这个是在HTML代码中写的,指令声明代码中没有显示调用子指令方法的代码,很简便。
在<page-tab>中属性var的值表示会在当前page-tabbat的作用域中创建一个pageTab对象,通过$scope.pageTab可以调用page-tab指令中暴露的方法;
接下来看page-tab指令:
<div> <ons-carousel var="pageTabCarousel" ons-postchange="console.log('Changed to ' + $event.activeIndex)" overscrollable style="height: 64px" swipeable auto-scroll item-width="{{itemWidth}}"> <ons-carousel-item style="background:transparent" ng-repeat="item in list" class="page-tab-carousel-item" ng-click="onClickItem($index)"> <div class="page-tab" ng-class="{true:'emphasize',false:'normal'}[currentIndex == $index]" ng-click="onClick({index:$index,item:item})"> <div class="page-tab page-tab-content"> <ons-icon icon="{{item.icon}}"></ons-icon> <span ng-bind="item.name"></span> </div> </div> </ons-carousel-item> </ons-carousel> </div>
js声明指令代码:
app.directive('pageTab', replace: true, templateUrl: 'js/directives/page-tab/page.tab.template.html', scope: { list: '=data', onClick: '&onClick', itemNum: '@' },$transclude) { angular.element(document).ready(function () { pageTabCarousel.setActiveIndex($scope.currentIndex); }); $scope.onClickItem = function (n) { $scope.currentIndex = n; n >= 1 && n--; pageTabCarousel.setActiveIndex(n); }; $scope.currentIndex = 0; $scope.$parent.$parent[$attrs.var] = { setActiveIndex: function (index) { $scope.onClickItem(index); } }; } ],attrs) { var num = attrs.itemNum - 0; scope.itemWidth = (100/num).toFixed(0)+"%"; } } });
这里只讲重点,在这个page-tab子指令中,通过这一段代码:
$scope.$parent.$parent[$attrs.var] = { setActiveIndex: function (index) { $scope.onClickItem(index); } };
向父指令提供了一个对象,对象中的方法以及属性就是子指令暴露的方法,父指令可以通过调用这个对象的方法通知子指令做出相应的改变;
同理,page-content也是一样:
<ons-page style=" position: absolute; bottom: 0; top: 64px; left: 0; right: 0px; "> <ons-carousel var="pageContentCarousel" ons-postchange="onCarouselChange()" fullscreen swipeable auto-scroll overscrollable> <ons-carousel-item ng-repeat="item in list"> <ng-include src="item.url"></ng-include> </ons-carousel-item> </ons-carousel> </ons-page>
app.directive('pageContent', templateUrl: 'js/directives/page-content/page-content.template.html', postchange:'&postchange' },$transclude) { angular.element(document).ready(function () { console.log('pageContent ready'); }); $scope.onCarouselChange = function () { $scope.postchange({ index:pageContentCarousel.getActiveIndex() }); }; $scope.$parent.$parent[$attrs.var] = { setActiveIndex:function (index) { pageContentCarousel.setActiveIndex(index); } }; } ],attrs) { } } });
在子指令中通知父指令做出变化的方法是,通过scope的@(对父作用域方法的引用)的方式,直接调用父指令传递的方法,调用方法的时候如果要传递参数,需要显示传递参数的key以及value,比如代码中的
$scope.onCarouselChange = function () { $scope.postchange({ index:pageContentCarousel.getActiveIndex() }); };