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');
            });
        };
    }]);

      

+3


source to share


1 answer


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.

+1


source







All Articles