控制器间通信,角度方式

时间:2015-01-17 18:02:42

标签: angularjs angularjs-scope

我试图找出"首选"或者" angular-way"在控制器/指令之间共享属性或状态。有几种方法可以实现这一点,但我希望保持最佳实践。以下是一些如何实现这一点的平庸例子:


1。使用$ scope。$ watch

// The parent controller/scope
angular.module('myModule').controller('parentController', ['$scope', function($scope) {
    $scope.state = {
        myProperty: 'someState'; // Default value to be changed by some DOM element
    };
}]);

// The child controller/scope.
angular.module('myModule').controller('childController', ['$scope', function($scope) {
    $scope.$watch('state.myProperty', function (newVal) {
        // Do some action here on state change
    });
}]);

编辑:根据以下答案,这是不好的做法,应该避免。它是不可测试的,并且会产生不必要的DOM依赖性。


2。使用$ broadcast

// The parent controller
angular.module('myModule').controller('parentController', ['$scope', function($scope) {
    var myProperty = 'someState';
    $scope.setState = function (state) {
        myProperty = state; // Set by some other controller action or DOM interaction.
        $scope.$broadcast('stateChanged', state); // Communicate changes to child controller
    }
}]);

// The child controller.
angular.module('myModule').controller('childController', ['$scope', function($scope) {
    $scope.$on('stateChanged', function (evt, state) {
        // Do some action here
    }
}]);

编辑:同样糟糕的做法,因为您需要知道控制器在DOM中的位置,以确定使用$ broadcast(在DOM下)或$ emit(在DOM上)的天气


第3。使用服务

angular.module('myModule').factory('stateContainer', [function () {
    var state = {
            myProperty: 'defaultState'
        },
        listeners = [];

    return {
        setState: function (newState) {
            state.myProperty = newState;
            angular.forEach(listeners, function (listener) {
                listener(newState);
            });
        },
        addListener: function (listener) {
            listeners.push(listener);
        }
    }
}]);

// The parent controller
angular.module('myModule').controller('parentController', ['$scope', 'stateContainer', function($scope, stateContainer) {
    $scope.setState = function (state) {
        stateContainer.setState(state);
    };
}]);

// The child controller.
angular.module('myModule').controller('childController', ['$scope', 'stateContainer', function($scope, stateContainer) {
    stateContainer.addListener(function (newState) {
        // Do some action here
    });
}]);

我可能错过了一些方法,但你明白了。我试图找到最佳方法。虽然冗长,但我个人倾向于列表中的#3。但我来自Java和jQuery背景,听众被广泛使用。

编辑:以下答案非常有见地。一个讲述了使用require指令配置在父/子指令之间共享状态。另一个是直接与范围共享服务或服务属性的讨论。我相信,根据需要,他们在Angular的最佳实践中是正确的。

2 个答案:

答案 0 :(得分:5)

如果正确完成,其中任何一个都会有效,但服务上的变体是AFAIK的首选方式。

问题是,在服务案例中你甚至需要一个监听器吗? Angular本身会更新任何视图(这是控制器的目的),那么为什么需要监听器或监听?只需更改要更改的视图的值本身就足够了。

app.factory('stateService',function() {
  return {
     myState: "foo"
  }
})
.controller('one',function($scope,stateService) {
    $scope.changeState = function() {
      stateService.myState = $scope.state;
    };
})
.controller('two',function($scope,stateService) {
    $scope.svc = stateService;
})

然后,您可以在视图中执行以下操作(不完整):

<div ng-controller="one">
  <input name="state" ng-model="state"></input>
  <button type="submit" ng-click="changeState()">Submit</button>
</div>
<div ng-controller="two">{{svc.myState}}</div>

事实是,你甚至不需要通过按钮和功能去那么远。如果你只是将ng-model绑在一起就可以了:

<div ng-controller="one">
  <input name="state" ng-model="svc.myState"></input>
</div>
<div ng-controller="two">{{svc.myState}}</div>

尝试以下jsfiddle http://jsfiddle.net/cwt9L6vn/1/

答案 1 :(得分:2)

AngularJS中没有父控制器和子控制器。只有父指令和子指令,但不是控制器。指令可以有一个控制器,它作为API暴露给其他指令。

控制器与DOM层次结构无关,因此无法生成子级。他们也没有创造自己的范围。因此,您永远不知道是否必须$broadcast$emit与其他控制器交谈。

如果您从控制器开始使用$broadcast,那么您将不知道其他控制器是向上还是向下 。当人们开始做像$rootScope.$broadcast(..)这样的非常糟糕的做法之类的东西时。

您正在寻找的是require其他指令的指令。

var app = angular.modeul('myApp',[]);
// use a directive to define a parent controller
app.directive('parentDir',function() {
     return {
         controller: function($scope) {
             this.myFoo = function() {
                alert("Hello World");
             }
         }
     });
// use a directive to enforce parent-child relationship
app.directive('childDir',function() {
     return {
          require: '^parentDir',
          link: function($scope, $el, $attr, parentCtrl) {
             // call the parent controller
             parentCtrl.myFoo();
          }
     });

使用指令的require功能有两个重要的事情。

  1. 如果关系不是可选的,Angular会强制执行该关系。
  2. 将父控制器注入子链接功能。
  3. 无需$broadcast$emit

    另一个有效的选择是使用指令来公开API。

    // this directive uses an API
    app.directive('myDir',function() {
         return {
              scope: { 
                'foo': '&'
              },
              link: function($scope, $el, $attr) {
                 // when needed, call the API
                 $scope.foo();
              }
         });
    
     // in the template
     <div ng-controller="parentController">
         <div my-dir foo="parentController.callMyMethod();"></div>
     </div>