Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating an "App-Wide" Message System in Angular

So I want to create a directive that outputs a global message.

Requirements of directive...

  1. This directive message can be updated from any controller.

  2. When the message is updated from any controller so is the directive consequently the view.

  3. Clear the Message after the view is destoryed

So far I am doing this by creating a directive and service that work together. Problem is Im not able to update the view when the message is update from inside other controllers

If someone could direct me on how to proceed that would be swell. What 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');
            });
        };
    }]);
like image 658
JerryA Avatar asked Aug 13 '26 01:08

JerryA


1 Answers

Use $rootscope.$emit to send messages from your controllers (and even services) and use $rootScope.$on to receive them in your directive.

You must remove the listener on the directive's scope destruction 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 persisted outside of 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 remove the message added when the view is destroyed. With this method you could add a second emit to the message on the NewPlateController scope's destruction with an empty string message.

This does not cover dynamically adding or removing the directive to the DOM. For that you could use a service to add and later remove the directive tag. This is how module's like ngToast and ui.boostrap's modal service work. Using one of them may be more appropriate for what you want to accomplish.

like image 103
meticoeus Avatar answered Aug 16 '26 20:08

meticoeus



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!