最近做Angular 项目,经常遇到的一个问题是控制器之间的通信问题。这篇文章打算总结一下这部分的使用经验,有借鉴别人的部分,也有自己的总结,我会尽量说的明白些~如果我的理解有纰漏,请帮忙之处,谢谢。
问题背景
我最开始写功能的时候,只是定义了一个 mainController,就只有这一个控制器。由于是一个单页面应用,所以也比较ok,各种功能的实现也没啥大的问题。但是随着代码的深入,我老是觉得把所有东西都写到$scope上面很不妥,这会导致 $scope 越来越臃肿,而且命名也是个问题,万一不小心名字重复覆盖了呢?接下来说下我用到的解决方案。。。
第一种解决方案
很简单,就是我在$scope下面创建了几个不同的对象,比如$scope.data 就用来存放用到的公共数据,$scope.global 就是使用的一些公共方法。这样倒是可行,但他们两个对象后面也会变得比较臃肿,感觉这并不是什么可靠的方式。
第二种
这个时候问了下我们组长,他的建议是mainController下面,再让我自己去分几个controller。这倒是提醒了我,于是我重新组织了页面,划分了各个不同的模块,每一个模块对应一个controller。比如购买页面主体部分有promoteController 和 payController,侧边栏是sidebarController等等。每个controller 就负责自己那部分的页面渲染和事件绑定。
这样分割模块下来感觉整体清晰了很多,不再想之前咋样各个部分的代码逻辑都杂糅在一起。这就是模块化编程的一种优势吧。
综上,我最终选择了分模块,设置控制器这样一种方式,然后他们上层有一个主模块,整体控制的方式。这样就出现本文涉及的主要问题,控制器之间如何通信。。。。
问题的出现
项目中这样一个小功能,点开一个小三角,弹出提示信息框,再点,就关闭这个提示框!相信前端朋友们很多都做过这个小效果,很简单。但是还有功能,就是除了这个小三角之外的地方,如果触发了点击事件,而且提示框在显示的话,要关闭这个提示框。再说下我的解决方案。。。
控制器继承方式
我把小弹窗的处理函数比如 A(),定义到 mainController,然后如果小三角点击事件触发,则在相应的子控制器中,出发A()。因为子控制器是可以继承得到mainController的方法,
所以可以子控制可以直接调用。
但是这种方式给我感觉有些混乱,最主要的还是,这应该是子控制器自己的方法,不应该上升到 mainController。
基于事件的方式
Angular 中提供了$on,$broadcast,$emit 这几种方式,我当时也是主要参考了后面列出的几篇文章。一使用还是很容易就实现了。
通过这种方式,我把提示框显示留在了子控制器。具体实现代码类似:
mainController 直接调用
$broadcast( 'closeTip' );
子控制器接收
$on( 'closeTip', function( $event ) {
$scope.hideTip = false;
$event.stopPropagation(); // 停止冒泡
} );
这样当全局接收到点击事件的时候,就是把关闭这个提示框的信号广播出去。那么相应模块的子控制器接收到了之后,就会调用自己的方法关闭提示框。那么这里还用到了 $event.stopPropagation(); 就终止了事件继续冒泡,不让点击事件再次传递。。。( 这部分停止冒泡是为了处理单纯小三角的点击事件,因为它的点击会传递到mainController,而mainController上面默认的点击事件是关闭提示框。所以不能让这部分传递过去 )
总结
我这部分处理主要参考的就是参考资料1 && 3,我这里对1的学习做个总结。首先感谢这篇分享,学到了很多,还有实例代码!主要用了文章中前两种实现方式。
1 继承的通信方式
单独一个基本属性,如定义一个变量,子级控制器能够从父级继承得到,而且父级修改会影响子级,但子级修改并不影响父级,而且子级如果有属性和父级同名,那么子级默认使用自己的变量。这和js 的作用域一样。如果要父级和子级控制器共享一个值,既父级子级修改都能相互影响,这就需要定义一个对象。因为对象是引用类型。。。
2 基于事件$broadcast $emit 看参考资料2,使用 $broadcast 貌似有性能问题,不知现在的Angular版本是否解决。。。。感觉广播的方式确实会很降低效率,还是 $emit 好!
3 服务的方式,我虽然没用这种方式实现,但感觉这种更倾向于模块化,比上面的实现思路更加清晰。。。而且更加通用
这是目前关于控制器通信的一些认识。实现控制器通信方式这些文章介绍的很详细,大家可以参考下!欢迎指正我文章的错误~~