控制器间通信,angular 方式
Inter-Controller communication, the angular way
我正在尝试找出 "preferred" 或 "angular-way" 在 controllers/directives 之间共享属性或状态的方法。有几种方法可以实现这一点,但我想保持最佳实践。以下是如何实现这一点的一些普通示例:
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
指令配置在 parent/child 指令之间共享状态。另一个谈到直接向范围共享服务或服务属性。我相信,根据需要,他们在 Angular 中什么是最佳实践或什么不是最佳实践都是正确的。
如果正确完成,这些中的任何一个都可以工作,但服务的变体是 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/
AngularJS 中没有 parent 和 child 控制器。只有 parent 和 child 指令,但没有控制器。指令可以有一个控制器,它作为 API 暴露给其他指令。
控制器与 DOM 层次结构无关,因此它们不能有 children。他们也不创建自己的范围。所以你永远不知道你是否必须 $broadcast
或 $emit
才能与其他控制器交谈。
如果您开始从一个控制器使用 $broadcast
,那么您将陷入困境,不知道另一个控制器是 up 还是 下来。那是人们开始做 $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
功能可以做两件重要的事情。
如果关系不是可选的,- Angular 将强制执行关系。
- parent控制器注入childlink函数。
不需要$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>
我正在尝试找出 "preferred" 或 "angular-way" 在 controllers/directives 之间共享属性或状态的方法。有几种方法可以实现这一点,但我想保持最佳实践。以下是如何实现这一点的一些普通示例:
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
指令配置在 parent/child 指令之间共享状态。另一个谈到直接向范围共享服务或服务属性。我相信,根据需要,他们在 Angular 中什么是最佳实践或什么不是最佳实践都是正确的。
如果正确完成,这些中的任何一个都可以工作,但服务的变体是 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/
AngularJS 中没有 parent 和 child 控制器。只有 parent 和 child 指令,但没有控制器。指令可以有一个控制器,它作为 API 暴露给其他指令。
控制器与 DOM 层次结构无关,因此它们不能有 children。他们也不创建自己的范围。所以你永远不知道你是否必须 $broadcast
或 $emit
才能与其他控制器交谈。
如果您开始从一个控制器使用 $broadcast
,那么您将陷入困境,不知道另一个控制器是 up 还是 下来。那是人们开始做 $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
功能可以做两件重要的事情。
-
如果关系不是可选的,
- Angular 将强制执行关系。
- parent控制器注入childlink函数。
不需要$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>