Creating an "App-Wide" Message System in Angular
So, I want to create a directive that outputs a global message.
Directive requirements ...
-
This directive message can be updated from any controller.
-
When a message is updated from any controller, hence it is a directive, hence a view.
-
Clear message after view is destroyed
So far I have been doing this by creating a directive and a service that work together. The problem is I cannot update the view when the message is updated from other controllers.
If anyone can guide me on how to proceed, it will be a wave. How about using $ rootScope and broadcasting?
app.directive("alertMsg", ['MsgService', function(MsgService) {
return {
restrict: "E",
scope: true,
template: '{{msg}}', // this string is the html that will be placed inside the <alert-msg></alert-msg> tags.
link: function (scope, $element, attrs) {
scope.msg = MsgService.getAlertMsg(); //set msg to be available to the template above <alert-msg>{{msg}}</alert-msg>
scope.$on("$destroy", function(){ //when the <alert-msg> view is destroyed clear the alert message
MsgService.clearAlertMsg();
});
}
};
}]);
app.service('MsgService', function() {
this.alertMsg = '';
this.getAlertMsg = function(){
return this.alertMsg;
};
this.setAlertMsg = function(string) {
this.alertMsg = string;
};
this.clearAlertMsg = function(){
this.alertMsg = '';
};
});
app.controller('NewPlateController', ['urlConfig', '$scope', '$http', '$location', 'MsgService', '$routeParams', function(urlConfig, $scope, $http, $location, MsgService, $routeParams) {
$scope.plate = {license_plate: $routeParams.plate, state: 'default-state'};
// create new plate via json request
$scope.createPlate = function(){
$http.post(urlConfig.rootUrl+"/plates.js", $scope.plate).success(function(data) {
$scope.plateInfo = data;
MsgService.setAlertMsg('Plate Sucessfully Created'); //Need to update the directive to actual show this update
$location.path('/plate/'+$scope.plateInfo.plate_id);
// http error: display error messages
}).error(function(data,status,headers,config) {
$scope.errors = data;
$('#new-plate-errors').slideDown('fast');
});
};
}]);
source to share
Use $ rootscope. $ emit to send messages from your controllers (and even services) and use $ rootScope. $ to get them in your directive.
You must remove the listener when destroying the directive's scope or you will have a memory leak.
app.directive("alertMsg", ['$rootScope', function($rootScope) {
return {
restrict: "E",
scope: true,
template: '{{msg}}', // this string is the html that will be placed inside the <alert-msg></alert-msg> tags.
link: function (scope, $element, attrs) {
var _unregister; // store a reference to the message event listener so it can be destroyed.
_unregister = $rootScope.$on('message-event', function (event, message) {
scope.msg = message; // message can be value, an object, or an accessor function; whatever meets your needs.
});
scope.$on("$destroy", _unregister) //when the <alert-msg> view is destroyed remove the $rootScope event listener.
}
};
}]);
app.controller('NewPlateController', ['urlConfig', '$scope', '$http', '$location', '$rootScope', '$routeParams', function(urlConfig, $scope, $http, $location, $rootScope, $routeParams) {
$scope.plate = {license_plate: $routeParams.plate, state: 'default-state'};
// create new plate via json request
$scope.createPlate = function(){
$http.post(urlConfig.rootUrl+"/plates.js", $scope.plate).success(function(data) {
$scope.plateInfo = data;
$rootScope.$emit('message-event', 'Plate Sucessfully Created'); //use $emit, not $broadcast. Only $rootscope listeners are called.
scope.$on("$destroy", function() { // remove the message when the view is destroyed.
$rootScope.$emit('message-event', "");
});
$location.path('/plate/'+$scope.plateInfo.plate_id);
// http error: display error messages
}).error(function(data,status,headers,config) {
$scope.errors = data;
$('#new-plate-errors').slideDown('fast');
});
};
}]);
The message is not saved outside the directive, so it will be removed when its scope is destroyed.
Edit: Adding a JSFiddle showing a working example: http://jsfiddle.net/kadm3zah/
Edit 2: I missed the requirement to remove the delete post added when the view was destroyed. With this method, you can add a second signal to the NewPlateController scoped destruction message with an empty string message.
This does not apply to dynamically adding or removing a directive in the DOM. To do this, you can use a service to add and then remove the directive tag. This is how a module works, for example the ngToast modal service and ui.boostrap. Using one of these may be more appropriate for what you want to accomplish.
source to share