4

I've multiple controllers in my application, where I have some duplicate code like:

$scope.alert = null;

$scope.addAlert = function (message) {
    $scope.alert = { type: 'danger', msg: message };
};

$scope.clearAlerts = function () {
    $scope.alert = null;
};

What is the recommended way sharing these scope functions and variables in AngularJS? Using controller inheritance?

Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
dhrm
  • 14,335
  • 34
  • 117
  • 183
  • Using services https://docs.angularjs.org/guide/services – Norbor Illig Mar 23 '15 at 14:18
  • possible duplicate of [Global variables in AngularJS](http://stackoverflow.com/questions/11938380/global-variables-in-angularjs) – Antiga Mar 23 '15 at 14:18
  • Controller inheritance is not a good practice. Use `factory` and `service` for that – valverde93 Mar 23 '15 at 14:19
  • possible duplicate of [AngularJS - single alert div for multiple controllers](http://stackoverflow.com/questions/24075447/angularjs-single-alert-div-for-multiple-controllers) – Roberto Linares Mar 23 '15 at 14:27
  • @RobsonGilli But if I use a service, then the alert variable in the service will be shared between all controllers using it. Right? I need each controller to have its own `alert`. – dhrm Mar 25 '15 at 16:28
  • 1
    In my case I created an alert service that receives from the controllers the type, title and message, and presents the alert to the user based on that information. the answer from @pankajparkar seems to be the way to go. The service will only be accessible to the controllers you inject the dependency in.... – Norbor Illig Mar 25 '15 at 17:43

2 Answers2

5

Create a one controller and then place common methods inside that controller scope. So that you can use that scope anywhere else and get access to method inside controller.

Controller

app.controller('commonCtrl', function($scope) {
    $scope.alert = null;

    $scope.addAlert = function(message) {
        $scope.alert = {
            type: 'danger',
            msg: message
        };
    };

    $scope.clearAlerts = function() {
        $scope.alert = null;
    };
});

Thereafter use scope of this controller by inject it using $controller, and then inside curly brace you could assign common controller scope to the current scope of controller.

Utilization of Common Controller

app.controller('testCtrl', function($scope, $controller) {
    //inject comomon controller scope to current scope , 
    //below line will add 'commonCtrl' scope to current scope
    $controller('commonCtrl', { $scope: $scope }); 
    //common controller scope will be available from here

});

Or more precise way would be using common sharable service, that exposed two method and alert data, you can use this service method by injecting service name inside your controller.

Service

app.service('commonService', function($scope) {
    this.alert = null;

    this.addAlert = function(message) {
        this.alert = {
            type: 'danger',
            msg: message
        };
    };

    this.clearAlerts = function() {
        this.alert = null;
    };
});

Utilization of service inside Controller

app.controller('testCtrl', function($scope, commonService) {

  console.log(commonService.alert);
  commonService.addAlert("Something");
  console.log("Updated Alert" + commonService.alert);

});

Hope this has cleared your concept, Thanks.

Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
  • You should use a service not a controller – trees_are_great Mar 23 '15 at 14:26
  • How can I add a service in AngularJS 1.3+? It seems that it's done using a factory, but then all my controllers will share the alert. I need each controller using my service, to have it own instance. – dhrm Mar 25 '15 at 16:14
  • @DennisMadsen If your requirement is like such, then do create a new controller would be better way (as I mentioned in 1st approach)..rather than creating a service which will initialized new object & give new copy of variable, service/factory does mean that you should use them for shareable purpose – Pankaj Parkar Mar 26 '15 at 05:21
0

My own solution for this use case was to define a type of Observer Pattern.

The code was structured in the following way:

var app = angular.module('testModule', []);
app.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
         */
         };

         /*
          * Register this object as an event Listener. Possibly supply an event key, and listener id to enable more resuse
          */
         alertService.register(this);

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


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

The alertShowingController is a simple example of how all controllers can simply inject the alertService and generate an event.

My own implementation is more elaborate in that it uses separate event keys to allow the controllers to generate other event notifications.

I could then define a single div that was in a fixed position at the top of the page that would dispay bootstrap alerts.

<div my-alerts ng-repeat="alert in alertList" type="{{alert.type}}" close="closeAlert(alertList, $index)">{{alert.msg}}</div>
APD
  • 1,459
  • 1
  • 13
  • 19
  • In your example it seems that alerts are shared between all controllers, right? I need each controller to have it's own alert. – dhrm Mar 25 '15 at 16:28
  • I created a plunker http://plnkr.co/edit/BRFAGyeEN29SESVzLPPJ?p=preview, which may help for what you are trying to do – APD Mar 25 '15 at 18:44