我们正在使用树型导航元素,需要允许其他指令/控制器知道:
我正在尝试确定处理此问题的最佳角度方式。
到目前为止,我们一直在触发整个应用程序可以监听的事件 - 不太理想,但确保没有任何硬编码直接与组件通信。
但是,我们现在需要在激活另一个组件时获取当前选择。事件不会飞。
所以我正在考虑一个服务,一些单例保存当前选择,可以直接由树更新,并且可以从需要它的任何人那里读取。
然而,这提出了一些其他问题:
$watch
服务的nodeId? $watch
,似乎我应该直接暴露对象 - 所以除非我想使所需的$watch
代码复杂化,否则使用getter / setter将不起作用?我担心的一部分是,这将允许任何组件设置值,这有意不是我们允许的 - 更改不会影响树,但会使服务失效来自真实值的值,并将触发无效$watches
。
答案 0 :(得分:0)
实现getter不应该导致复杂的$ watcher:
服务:
angular.service('myService', function() {
var privateVar = 'private';
return {
getter: function() {
return privateVar;
};
});
控制器:
angular.controller('myController', function(myService){
$scope.watch(myService.getter, function(){
//do stuff
};
});
请参阅此plunker:http://plnkr.co/edit/kLDwFg9BtbkdfoSeE7qa?p=preview
答案 1 :(得分:0)
我认为使用服务应该有效,而且你不需要任何观察者。
在我的演示文稿中或fiddle中,我添加了以下内容:
sharedData
- 选择和项目sharedDataEvents
的另一项服务。要在component2
中显示值,我使用了单向绑定,因此该组件无法更改选择。
同样从事件中分离数据可防止组件更改选择。因此,只有MainController
和Component1
才能更改选择。
如果您打开浏览器控制台,则可以看到正在运行的侦听器。只有component3
的监听器正在做某事(在3次选择更改后它会发出警报)其他人只是将新选择记录到控制台。
angular.module('demoApp', [])
.controller('MainController', MainController)
.directive('component1', Component1)
.directive('component2', Component2)
.directive('component3', Component3)
.factory('sharedData', SharedData)
.factory('sharedDataEvents', SharedDataEvents);
function MainController(sharedData) {
sharedData.setItems([{
id: 0,
test: 'hello 0'
}, {
id: 1,
test: 'hello 1'
}, {
id: 2,
test: 'hello 2'
}]);
this.items = sharedData.getItems();
this.selection = this.items[0];
}
function Component1() {
return {
restrict: 'E',
scope: {},
bindToController: {
selection: '='
},
template: 'Comp1 selection: {{comp1Ctrl.selection}}'+
'<ul><li ng-repeat="item in comp1Ctrl.items" ng-click="comp1Ctrl.select(item)">{{item}}</li></ul>',
controller: function($scope, sharedData, sharedDataEvents) {
this.items = sharedData.getItems();
this.select = function(item) {
//console.log(item);
this.selection = item
sharedData.setSelection(item);
};
sharedDataEvents.addListener('onSelect', function(selected) {
console.log('selection changed comp. 1 listener callback', selected);
});
},
controllerAs: 'comp1Ctrl'
};
}
function Component2() {
return {
restrict: 'E',
scope: {},
bindToController: {
selection: '@'
},
template: 'Comp2 selection: {{comp2Ctrl.selection}}',
controller: function(sharedDataEvents) {
sharedDataEvents.addListener('onSelect', function(selected) {
console.log('selection changed comp. 2 listener callback', selected);
});
},
controllerAs: 'comp2Ctrl'
};
}
function Component3() {
//only listening and alert on every third change
return {
restrict: 'E',
controller: function($window, sharedDataEvents) {
var count = 0;
sharedDataEvents.addListener('onSelect', function(selected, old) {
console.log('selection changed comp. 3 listener callback', selected, old);
if (++count === 3) {
count = 0;
$window.alert('changed selection 3 times!!! Detected by Component 3');
}
});
}
}
}
function SharedData(sharedDataEvents) {
return {
selection: {},
items: [],
setItems: function(items) {
this.items = items
},
setSelection: function(item) {
this.selection = item;
sharedDataEvents.onSelectionChange(item);
},
getItems: function() {
return this.items;
}
};
}
function SharedDataEvents() {
return {
changeListeners: {
onSelect: []
},
addListener: function(type, cb) {
this.changeListeners[type].push({ cb: cb });
},
onSelectionChange: function(selection) {
console.log(selection);
var changeEvents = this.changeListeners['onSelect'];
console.log(changeEvents);
if ( ! changeEvents.length ) return;
angular.forEach(changeEvents, function(cbObj) {
console.log(typeof cbObj.cb);
if (typeof cbObj.cb == 'function') {
// callback is a function
if ( selection !== cbObj.previous ) { // only trigger if changed
cbObj.cb.call(null, selection, cbObj.previous);
cbObj.previous = selection; // new to old for next run
}
}
});
}
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.js"></script>
<div ng-app="demoApp" ng-controller="MainController as ctrl">
<p>Click on a list item to change selection:</p>
<component1 selection="ctrl.selection"></component1> <!-- can change the selection -->
<component2 selection="{{ctrl.selection}}"></component2>
<component3></component3>
</div>