1

Say i have the following factory:

app.factory("categoryFactory", function (api, $http, $q) {    
    var selected = null;
    var categoryList = [];
    return {
        getList: function () {
            var d = $q.defer();
            if(categoryList.length  <= 0){
                $http.get(api.getUrl('categoryStructure', null))
                    .success(function (response) {
                        categoryList = response;
                        d.resolve(categoryList);
                    });
            }
            else
            {
                d.resolve(categoryList)
            }
            return d.promise;
        },
        setSelected: function (category) {
            selected = category;
        },
        getSelected: function () {
            return selected;
        }
    }    
});

now i have two controllers using this factory at the same time. Because of this both controllers has to be notified when updated for this i attempted the following:

    app.controller('DashboardController', ['$http', '$scope', '$sessionStorage', '$log', 'Session', 'api','categoryFactory', function ($http, $scope, $sessionStorage, $log, Session, api, categoryFactory) {

    $scope.selectedCategory = categoryFactory.getSelected();

}]);

While my other controller looks like this:

    app.controller('NavController', ['$http', '$scope', '$sessionStorage', '$log', 'Session', 'api', 'FileUploader', 'categoryFactory', function ($http, $scope, $sessionStorage, $log, Session, api, FileUploader, categoryFactory) {
    $scope.categories = [];

    categoryFactory.getList().then(function (response) {
        $scope.categories = response;
    });
    $scope.selectCategory = function (category) {
        categoryFactory.setSelected(category);
    }

}]);

how ever when the NavController changed the value it was not changed in the DashboardController

My question is how can i either watch or in another way get notified when the value changes?

Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
Marc Rasmussen
  • 19,771
  • 79
  • 203
  • 364

2 Answers2

2

You can use an observer pattern, like so:

app.factory("categoryFactory", function (api, $http, $q) {   
    // the list of callbacks to call when something changes
    var observerCallbacks = []; 

    // ...

    function notifyObservers() {
        angular.forEach(observerCallbacks, function(callback) {
            callback();
        });
    }

    return {
        setSelected: function (category) {
            selected = category;
            // notify the observers after you change the value
            notifyObservers();
        },
        registerObserver: function(callback) {
            observerCallbacks.push(callback);
        }
    }    
});

And then in your controllers:

app.controller('NavController', ['$http', '$scope', '$sessionStorage', '$log', 'Session', 'api', 'FileUploader', 'categoryFactory', function ($http, $scope, $sessionStorage, $log, Session, api, FileUploader, categoryFactory) {
    // ...

    // init
    (function() {
        categoryFactory.registerObserver(function() {
            categoryFactory.getList().then(function (response) {
                $scope.categories = response;
            });
        });
    })();

}]);

This way, any time setSelected is called, it calls each callback that you've registered in observerCallbacks. You can register these from any controller since factories are singletons and they will always be in the know.

Edit: just want to add that I may have put the notifyObservers() call in the wrong area (currently in setSelected) and that I may be putting the wrong update call in the controller (currently getList) but the architecture remains the same. In the registerObserver, put whatever you want to do when the values are updated and wherever you make changes that you want observers to know about call notifyObservers()

Tom
  • 7,640
  • 1
  • 23
  • 47
  • Hey tom worked like a charm however you need to remove "this" from your registerObserver function – Marc Rasmussen Jul 15 '15 at 15:05
  • Great Answer! Just FYI: an additional $scope.$apply() call was neccessary in my case to get my variables to update! :) – luQ Jul 27 '17 at 11:31
1

You could follow dot rule here so that prototypal inheritance will get followed.

Basically you need to have one object inside your service that will have selected variable, And will get rid of getSelected method.

Factory

app.factory("categoryFactory", function(api, $http, $q) {
    var categoryFactory = {};
    categoryFactory.getList = function() {
        var d = $q.defer();
        if (categoryList.length <= 0) {
            $http.get(api.getUrl('categoryStructure', null))
                .success(function(response) {
                    categoryList = response;
                    d.resolve(categoryList);
                });
        } else {
            d.resolve(categoryList)
        }
        return d.promise;
    }
    categoryFactory.setSelected = function(category) {
        categoryFactory.data.selected = category;
    }
    categoryFactory.data = {
        selected: null
    }
    return categoryFactory;
});

Controller

app.controller('DashboardController', ['$http', '$scope', '$sessionStorage', '$log', 'Session', 'api', 'categoryFactory',
    function($http, $scope, $sessionStorage, $log, Session, api, categoryFactory) {
        //this will provide you binding without watcher
        $scope.selection = categoryFactory.data;
    }
]);

And then use {{selection.selected}} on html part will update a value when changes will occur in selection.

Community
  • 1
  • 1
Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
  • hey mate thank you for your answer. Sadly it does not seem to work :S the value is not changed in the controller once the value is changed in the factory – Marc Rasmussen Jul 15 '15 at 14:58