老规矩,先上效果图:
首先说一下本文的重点:
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-pageclass="hg-agency-item"ng-controller="hgAgencyItemController"> <ons-toolbar> <divclass="left"> <ons-back-button></ons-back-button> </div> <divclass="center"> <spanng-bind="'这个是标题'"></span> </div> <divclass="right"> <ons-toolbar-button><ons-iconicon="ion-search"></ons-icon> </ons-toolbar-button> </div> </ons-toolbar> <ons-pageclass="c-p"> <page-tabbardata="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-tabdata="data"item-num="{{tabNum}}"on-click="pageContent.setActiveIndex(index)"var="pageTab"></page-tab> <page-contentdata="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指令:
html代码:
<div> <ons-carouselvar="pageTabCarousel"ons-postchange="console.log('Changed to ' + $event.activeIndex)"overscrollable style="height: 64px" swipeable auto-scroll item-width="{{itemWidth}}"> <ons-carousel-itemstyle="background:transparent"ng-repeat="item in list"class="page-tab-carousel-item"ng-click="onClickItem($index)"> <divclass="page-tab"ng-class="{true:'emphasize',false:'normal'}[currentIndex == $index]"ng-click="onClick({index:$index,item:item})"><divclass="page-tab page-tab-content"><ons-iconicon="{{item.icon}}"></ons-icon><spanng-bind="item.name"></span></div> </div> </ons-carousel-item> </ons-carousel></div>
js声明指令代码:
app.directive('pageTab', function () { return { restrict: 'E', replace: true, templateUrl: 'js/directives/page-tab/page.tab.template.html', scope: { list: '=data', onClick: '&onClick', itemNum: '@' }, controller: [ '$scope', '$element', '$attrs', '$transclude', function ($scope, $element, $attrs, $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);}}; } ], link: function (scope, element, 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-itemng-repeat="item in list"> <ng-includesrc="item.url"></ng-include> </ons-carousel-item> </ons-carousel></ons-page>
app.directive('pageContent', function () { return { restrict: 'E', replace: false, templateUrl: 'js/directives/page-content/page-content.template.html', scope: { list: '=data', postchange:'&postchange' }, controller: [ '$scope', '$element', '$attrs', '$transclude', function ($scope, $element, $attrs, $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);}}; } ], link: function (scope,element,attrs) { } }});
在子指令中通知父指令做出变化的方法是,通过scope的@(对父作用域方法的引用)的方式,直接调用父指令传递的方法,调用方法的时候如果要传递参数,需要显示传递参数的key以及value,比如代码中的
$scope.onCarouselChange = function () { $scope.postchange({ index:pageContentCarousel.getActiveIndex() });};
index就是需要传递的参数,在父指令方法中获取参数的也是一样,参数要一致: