I have scenario where I want to communicate between sibling controllers in different apps. So I created sample demo which uses publisher-subscriber service to broadcast and listen event. But the code subscribing to the event is in the controller. So I want to understand whether this is a best practice? What is the alternative way to achieve the same, give example?
I indicated following scenario –
controllerA broadcast event and controllerB and controllerC listen to it (1-Many)
var app = angular.module('app', []);
app.controller('controllerA', ['$scope', 'pubsubService', controllerA]);
function controllerA($scope, pubsubService) {
$scope.teamName = '';
$scope.changeTeam = function() {
pubsubService.Publish("changeNameEvent", {
filterTeam: $scope.teamName
});
};
}
app.controller('controllerB', ['$scope', 'pubsubService', controllerB]);
function controllerB($scope, pubsubService) {
var callbackNameChanged = function(message) {
$scope.team = message.filterTeam
};
pubsubService.Subscribe("changeNameEvent", $scope, callbackNameChanged);
}
app.controller('controllerC', ['$scope', 'pubsubService', controllerC]);
function controllerC($scope, pubsubService) {
var callbackNameChanged = function(message) {
$scope.team = message.filterTeam
};
pubsubService.Subscribe("changeNameEvent", $scope, callbackNameChanged);
}
app.factory("pubsubService", ["$rootScope", function($rootScope) {
var Publish = function(message, item) {
try {
$rootScope.$broadcast(message, {
item: item
})
} catch (e) {
console.log(e.message)
}
};
var Subscribe = function(message, $scope, handler) {
try {
$scope.$on(message, function(event, args) {
handler(args.item)
})
} catch (e) {
console.log(e.message)
}
};
return {
Publish: Publish,
Subscribe: Subscribe
}
}]);
Html Code:
<body class='container'>
<div ng-controller="controllerA">
<input data-ng-model="teamName" type="text" data-ng-change="changeTeam()" />
</div>
<div ng-controller="controllerB">controllerB - You typed: {{team}}
<br />
</div>
<div ng-controller="controllerC">controllerC - You typed:{{team}}</div>
</body>
After the analysis I come up with following solution to move subscription logic to a directive with "&" operator parameter that allows to invoke or evaluate an expression/function on the parent scope and keep controller code to minimum. As dumping things onto the controller is a bad idea 99% of the time. Unless it's a scope variable or a watch you can most likely abstract it into something else.
By implementing this way we can make code reusable, testable and modular.
app.directive('onChangeName', ['pubsubService', function(pubsubService) {
return {
restrict: 'EA',
scope: {
onNameChangeCallback: '&'
},
link: function(scope, element) {
pubsubService.Subscribe("changeNameEvent", scope, function(message) {
scope.onNameChangeCallback({
message: message.filterTeam
});
});
}
};
}]);
app.controller('controllerB', function($scope){
$scope.callbackNameChanged = function(message) {
$scope.team = message
};
});
app.controller('controllerC', function($scope){
$scope.callbackNameChanged = function(message) {
$scope.team = message
};
});
Html Code
<div ng-controller="controllerB">
<on-change-name on-name-change-callback="callbackNameChanged(message)"></on-change-name>
controllerB - You typed: {{team}}
<br />
</div>
<div ng-controller="controllerC">
<on-change-name on-name-change-callback="callbackNameChanged(message)"></on-change-name>
controllerC - You typed:{{team}}
</div>