V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
tianshilei1992
V2EX  ›  程序员

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

  •  
  •   tianshilei1992 ·
    shiltian · 2017-01-15 17:09:01 +08:00 · 5599 次点击
    这是一个创建于 2880 天前的主题,其中的信息可能已经有所发展或是发生改变。

    假设我在父控制器中放置一个 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 变量。因此想请教一下大家正确的方法,或者是在单页面应用中,如何正确地显示错误框。

    6 条回复    2017-01-16 19:22:38 +08:00
    alibabamama
        1
    alibabamama  
       2017-01-15 20:00:42 +08:00
    我也又遇到过类似的问题,用的`yeoman`生成的一套系统,所以整个架构和写法上和你贴出来的有点不同。我在 child state 通过$scope, 类似 `$scope.ParentController.alert`的形式去获取和更新的。
    gyteng
        2
    gyteng  
       2017-01-15 20:46:51 +08:00
    绑定到$scope 是一种方法,还可以自己写一个 service 来搞定多个 controller 之间共享数据的问题
    tianshilei1992
        3
    tianshilei1992  
    OP
       2017-01-16 07:17:16 +08:00 via iPhone
    @gyteng 诶?写给 service 如何来共享数据呀?我能想到的只是单向传递,如果 service 里面的值变了,如何保证 controller 的值也跟着改变呀?😄
    WittBulter
        4
    WittBulter  
       2017-01-16 07:39:30 +08:00 via iPhone
    手机写了一堆,忘了保存,待会换电脑来写。
    WittBulter
        5
    WittBulter  
       2017-01-16 16:21:30 +08:00   ❤️ 2
    这是典型的父子通信问题,但是并不符合你的业务逻辑。
    在 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
        6
    tianshilei1992  
    OP
       2017-01-16 19:22:38 +08:00
    @WittBulter 非常感谢,我先研究一下你给的那两个模版。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1104 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 19:03 · PVG 03:03 · LAX 11:03 · JFK 14:03
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.