Angularjs 控制器间通信,角度方式

Angularjs 控制器间通信,角度方式,angularjs,angularjs-scope,Angularjs,Angularjs Scope,我试图找出在控制器/指令之间共享属性或状态的“首选”或“角度方式”。有几种方法可以实现这一点,但我希望保持最佳实践。以下是如何实现这一点的一些平庸例子: 1。使用$scope.$watch // The parent controller/scope angular.module('myModule').controller('parentController', ['$scope', function($scope) { $scope.state = { myProp

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


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
指令配置在父/子指令之间共享状态。另一种方法是直接向作用域共享服务或服务属性。我相信,根据需要,他们都是正确的,在什么是或不是最佳实践的角度

如果操作正确,这些方法中的任何一种都会起作用,但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;
})
然后,您可以在视图中执行以下操作(不完整):


尝试下面的JSFIDLE

AngularJS中没有父控制器和子控制器。只有父指令和子指令,但没有控制器。指令可以有一个控制器,该控制器作为API公开给其他指令

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();
          }
     });
控制器与DOM层次结构无关,因此它们不能有子级。它们也不创建自己的范围。因此,您永远不知道是否必须
$broadcast
$emit
才能与其他控制器通话

如果您从一个控制器开始使用
$broadcast
,那么您将无法知道另一个控制器是向上还是向下。这时人们开始做一些事情,比如
$rootScope.$broadcast(..)
,这是一种非常糟糕的做法

您要查找的是
需要
其他指令的指令

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
功能可以做两件重要的事情

  • 如果不是可选的,Angular将强制执行该关系
  • 父控制器被注入子链接函数
  • 无需
    $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>
    
    //此指令使用API
    app.directive('myDir',function(){
    返回{
    范围:{
    “foo”:“和”
    },
    链接:函数($scope、$el、$attr){
    //需要时,调用API
    $scope.foo();
    }
    });
    //在模板中
    
    没有想到将整个服务应用于作用域。很好的一个!:-)这被称为作用域出血,这是一个非常糟糕的做法。那么@MaximeMorin,服务中的访问器,你有什么建议吗?然后我又回到了每次属性更改时都让侦听器触发的状态。我相信视图不应该知道数据来自何处。在本例中,视图知道服务。我认为控制器应该保持状态,然后视图将只使用控制器。视图:
    {{ctrl.myState}}
    (使用ControllerAs语法)在Ctrl:
    Ctrl.myState=myService.myState;
    中,控制器可以决定保留状态或原始引用的副本。这打开了保存/取消、验证检查的大门……是的,所有注释都是100%正确的;将服务直接公开给视图是不好的做法。这只是简单的检查ple表明它是有效的,但你是对的。但是,要小心使用Oystein的例子,因为如果它只是文本,你会失去一些观察。一般来说,你希望你的视图看到的是对象,这就是为什么我选择了最简单的路径。我将在上面更新它,但要注意“我需要一个点”,我意识到我使用了术语“父/子”这里是“控制器”。我的意思是父/子范围。我提出这个问题的原因是我想在不同的角度对象之间共享应用程序状态。我理解上面示例1的反对意见,但这不仅适用于指令(web组件),因为应用程序基础结构不仅仅包含指令……但是+1是对象间通信的附加选项。:-)关键是使对象尽可能彼此解耦。这提高了测试和可维护性。当控制器或指令依赖于
    $scope
    具有mai状态时受到某种神奇的外部影响,或者业务逻辑只有在
    $scope
    接收到未知广播公司的事件时才会执行,那么你会发现将来必须更改、测试和维护该代码。我完全同意,如果“神奇的外力”指的是$scope.$parent或$broadcast/$emit。但是(IMHO)是小型可重用组件。不是整个页面。我认为即使是指令也可以安全地将状态存储在服务中以供其他组件重用。这应该是可测试的,就像指令是用自己的状态存储分层设计的一样。我想我们彼此都同意,但使用的术语不同,所以听起来像是在讨论对不同的事情有不同的看法。
    // 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>