关于 AngularJS 中子控制器访问父控制器中元素的正确方法

2017-01-15 17:09:01 +08:00
 tianshilei1992

假设我在父控制器中放置一个 Bootstrap 风格的 alert 框,用于提示错误,类似如下效果: !()[https://ww1.sinaimg.cn/large/006tKfTcjw1fbrff61vqcj308q03pglq.jpg]

<body ng-controller="ParentController as parentCtrl">
  <!-- 这里有一个 alert ,用于显示错误 -->
  <div ng-show="parentCtrl.alert"></div>
  <div ng-controller="ChildController as childCtrl">
  </div>
</body>

在 js 文件中,我在控制器 ParentController 中定义了变量 alert

app.controller('ParentController', [function() {
  var self = this;
  self.alert = null;
}]);

app.controller('ChildController', [function() {
  // 那么这里应该如何访问到 ParentController 中的 alert 呢?
}]);

现在我想做的,是当 ChildController 中某些操作出现错误的时候,给 ParentController 中的 alert 赋值,这样那个错误 alert 框就可以显示,不知道应该如何写 ChildController 的代码呢?

现在有一个折中的办法是,将 ParentController 中的 alert 元素绑定到 $scope 中,即如下:

<body ng-controller="ParentController as parentCtrl">
  <!-- 这里有一个 alert ,用于显示错误 -->
  <div ng-show="alert"></div>
  <div ng-controller="ChildController as childCtrl">
  </div>
</body>

相应的 js 文件如下:

app.controller('ParentController', ['$scope', function($scope) {
  $scope.alert = null;
}]);

app.controller('ChildController', ['$scope', function($scope) {
  $scope.alert = {
    type: 'alert',
    info: 'Error...'
  };
}]);

但是现在的 AngularJS 不都推崇的是直接将要显示的数据绑定到控制器上,而不是向控制器中注入 $scope 变量。因此想请教一下大家正确的方法,或者是在单页面应用中,如何正确地显示错误框。

5410 次点击
所在节点    程序员
6 条回复
alibabamama
2017-01-15 20:00:42 +08:00
我也又遇到过类似的问题,用的`yeoman`生成的一套系统,所以整个架构和写法上和你贴出来的有点不同。我在 child state 通过$scope, 类似 `$scope.ParentController.alert`的形式去获取和更新的。
gyteng
2017-01-15 20:46:51 +08:00
绑定到$scope 是一种方法,还可以自己写一个 service 来搞定多个 controller 之间共享数据的问题
tianshilei1992
2017-01-16 07:17:16 +08:00
@gyteng 诶?写给 service 如何来共享数据呀?我能想到的只是单向传递,如果 service 里面的值变了,如何保证 controller 的值也跟着改变呀?😄
WittBulter
2017-01-16 07:39:30 +08:00
手机写了一堆,忘了保存,待会换电脑来写。
WittBulter
2017-01-16 16:21:30 +08:00
这是典型的父子通信问题,但是并不符合你的业务逻辑。
在 NG1 中,如果真的要使用父子通信,最优雅的办法是采用一个共享的服务来解决,当有涉及模板时,应创建一个指令,任何子控制器都通过相应的事件来触发指令。使用共享作用域的 scope 的方法实际上不好的,你代码中的父级作用域其实就是类似 rootScope ,当他们被越来越多的变量、对象占有时,很难通过控制器之间的切换累释放内存。其次当你需要调用他们的时候也会很难,特别是你的控制器代码被复用时,其他人很难知道需要在父级再做一件这样的事。这就违背了 NG1 中自治的哲学。

如果要达到这一点,至少要保证你的任何控制器不依赖于其他控制器,任何指令可以直接使用而无需在控制器中为它准备一些元数据,任何服务也是注入即使用。这其实是很难的,因为你要做一些额外的工作来达到这一点,比如强制自己不使用非当前作用域的变量、函数,通信采用服务与事件等等。但是它们带来的好处也显而易见。

业务逻辑中, Alert 这样的问题常常会带有一个基础的模板,我建议是将它包装成一个指令,使用 Alert 相应模板,然后在较高层作用域的模板中编译(让子作用域都可以享受),当你需要它显示时,在控制器中发出相应的事件来触发这个模板显示与隐藏。
还有一种更优雅的解决方案,新建一个 alertModule ,将控制器、指令、服务全部挂载在 alertModule 上,再让业务的 module 依赖它,即可以使用相应的方法来触发这个弹窗模块。

你可以参考一下我写的两个模板,它们分别是挂载 window 和挂载 module 方式。
https://github.com/WittBulter/sendAlert
https://github.com/WittBulter/angular-mobile-tips
angular-mobile-tips 使用了很多有意思的技巧,是一个典型的 NG1 插件,可以尝试模仿它写一个。
tianshilei1992
2017-01-16 19:22:38 +08:00
@WittBulter 非常感谢,我先研究一下你给的那两个模版。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/334730

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX