AngularJs update directive after a call to service

2019-08-19 07:02发布

问题:

I'm trying to create resusable alert service, which I would call anywhere in my application with just:

alertService.showAlert('warning', 'something went wrong!');

For example after ajax call to backend api. Right now I'm using a factory and a directive, but It seems I'm doing something wrong, because the directive does not update after a call to showAlert method. Right now I have something like this:

var srv = angular.module('srv', []);
srv. factory('alertService', ['$timeout', function($timeout){
    var alertService = this;
        alertService.alertNeeded = false;
        alertService.alertClass = '';
        alertService.alertMessage = '';
        alertService.setAlertNeeded = function(){
            alertService.alertNeeded = true
        };
        alertService.setAlertClass = function(type){
            if(type === 'warning')
                alertService.alertClass = 'alert-warning';
            if(type === 'success')
                alertService.alertClass = 'alert-success';
            if(type === 'info')
                alertService.alertClass = 'alert-info';
            if(type === 'danger')
                alertService.alertClass = 'alert-danger';
        };
        alertService.setAlertMessage = function(message){
            alertService.alertMessage = message;
        };

        return {
            showAlert: function(class, msg){
                alertService.setAlertNeeded();
                alertService.setAlertClass(class);
                alertService.setAlertMessage(msg);
            }
        };
 }]).
 directive('myAlerts', ['alertService', function(alertService){
        return {
            restrict: 'A',
            template: '<div ng-class="alertClass" ng-show="alertNeeded">{{alertMessage}}</div>',
            link: function(scope){                   
                    scope.alertNeeded = alertService.alertNeeded;
                    scope.alertMessage = alertService.alertMessage;
                    scope.alertClass = alertService.alertClass;
            }
        }
    }]).
 controller('alertShowingController', ['$scope', 'alertService', function($scope, alertService){
     alertService.showAlert('warning', 'Warning alert!!!')   
 }]);

My code doesn't look exactly the same, but I just wanted to show what I'm trying to do: I want to call alertService.showAlert(...) from another controller in another module (which depends on srv module) and this way update the variables in myAlerts directive to show the proper alert.

The thing is after call to showAlert method The values are set, but within the directive code I'm getting alertService.alertNeeded as undefined.

I'm completely new to AngularJs, so maybe I'm getting something wrong, but I spent whole evening to make it work and I still have no idea what is the proper solution for this.

Please help!

回答1:

Your code has two different meanings for alertService. Inside the factory definition, it refers to the factory itself. Everywhere else, it refers to the object returned by the factory. The easiest way to move forward would be to add a few missing methods to the object returned by the factory:

return {
    showAlert: function(cssClass, msg){
        alertService.setAlertNeeded();
        alertService.setAlertClass(cssClass);
        alertService.setAlertMessage(msg);
    },

    alertClass: function() { return alertService.alertClass; },     
    alertMessage: function() { return alertService.alertMessage; },
    alertNeeded: function() { return alertService.alertNeeded; }
};

Then, change your directive's template so that it calls these functions on each digest cycle:

directive('myAlerts', ['alertService', function(alertService){
        return {
            restrict: 'A',
            template: '<div ng-class="alertClass()"' +
                      '     ng-show="alertNeeded()">' +
                      '  {{alertMessage()}}' +
                      '</div>',
            link: function(scope){                   
                scope.alertNeeded = alertService.alertNeeded;
                scope.alertMessage = alertService.alertMessage;
                scope.alertClass = alertService.alertClass;
            }
        }
}])

Then you should see your warning message. Try it in a fiddle.



回答2:

Here is a pattern that I used once before

var srv = angular.module('srv', []);
srv.factory('alertService', ['$timeout', function($timeout){
    var alertListeners = [];

    this.register = function (listener) {
        alertListeners.push(listener);
    };

    this.notifyAll = function (data) {
        for (// each listener in array) {
            var listenerObject = alertListeners[i];
            try { // do not allow exceptions in individual listeners to corrupt other listener processing
                listenerObject.notify(data);
            } catch(e) {
                    console.log(e);
            }   
        }
    };
 }]).
 directive('myAlerts', ['alertService', function(alertService){

     var alertDirectiveObserver = function($scope, alertService) {

         this.notify = function(data) {
            /*
             * TO DO - use data to show alert
             */
         };

         alertService.register(this);
     };


   return {
     restrict: 'A',
     template: '<div ng-class="alertClass" ng-show="alertNeeded">{{alertMessage}}</div>',
     controller: ['$scope', 'alertService', alertDirectiveObserver],
     link: function(scope){  
     }
    }
}]).
controller('alertShowingController', ['$scope', 'alertService',   function($scope, alertService){
    alertService.notifyAll({'warning', 'Warning alert!!!'})   
 ]);

Of course you should also cleanup by registering a function to delete objects on scope destroy.

eg

element.on('$destroy', function() {
    alertService.unregister(// some listener id);
});