We're using a tree-style navigation element which needs to allow other directives/controllers to know:
I'm trying to determine the best angular-way to handle this.
Until now, we've been firing an event the entire app can listen to - less than ideal but ensures nothing is hard-coded to communicate directly with the component.
However, we now have a need to obtain the current selection when another component is activated. An event won't fly.
So I'm considering a service, some singleton which holds the current selection and can be updated directly by the tree, and read from by anyone who needs it.
However, this present some other issues:
$watch
the service's nodeId? $watch
, it seems like I should expose the object directly - so using getters/setters won't work unless I want to complicate the needed $watch
code? Part of my concern is that this would allow any component to set the value and this is intentionally not something we'll allow - the change will not impact the tree but will de-sync the service values from the true values, and will fire invalid $watches
.
Implementing a getter should not lead to a complicated $watcher:
Service:
angular.service('myService', function() {
var privateVar = 'private';
return {
getter: function() {
return privateVar;
};
});
Controller:
angular.controller('myController', function(myService){
$scope.watch(myService.getter, function(){
//do stuff
};
});
See this plunker: http://plnkr.co/edit/kLDwFg9BtbkdfoSeE7qa?p=preview
I think using a service should work and you don't need any watchers for it.
In my demo below or in this fiddle I've added the following:
sharedData
that's storing the data - selection and items sharedDataEvents
with observer/listener pattern. To display the value in component2
I've used one-way binding, so that component can't change the selection.
Also separating data from events prevents a component from changing the selection. So only MainController
and Component1
can change the selection.
If you're opening the browser console you can see the listeners in action. Only listener of component3
is doing something (after 3 selection changes it will do an alert) the others are just logging the new selection to console.
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>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.