0

I currently have a modal (Angular-UI Modal) that, when opened for the first time, works perfectly. However, after I close it and then re-open it, things start acting a little strange.

The code:

(function () {
    'use strict';

    angular.module('app.modals').controller('personalizeNewsSubscriptionsModalCtrl', ['$scope', '$log', '$modalInstance', '$q', 'termStoreSvc', 'userNewsSubscriptionsSvc',
            function ($scope, $log, $modalInstance, $q, termStore, userSub) {
                //#region Variable declaration
                var allTopics = [],
                    userSubs = [],
                    subscriptions = new termStore('Manaaged Metadata Service', 'TS Today', 'Topic'),
                    userSubscriptions = new userSub('blackba');

                var allSubsLoaded = subscriptions.ready(function () {
                        allTopics = subscriptions.terms;
                    }),
                    userSubsLoaded = userSubscriptions.ready(function () {
                        userSubs = userSubscriptions.subscriptions;
                    });

                $log = $log.getInstance('app.modals.personalizeNewsSubscriptionsModalCtrl');
                //#endregion                

                $q.all([allSubsLoaded, userSubsLoaded]).then(function () {
                    // use allTopics and userSubs to create $scope.subscriptions
                    $scope.subscriptions = allTopics.map(function (obj) {
                        return {
                            Title: obj,
                            Selected: userSubs.indexOf(obj) !== -1
                        }
                    });
                    $log.debug('Subscriptions object loaded', $scope.subscriptions);
                });

                subscriptions.init();
                userSubscriptions.init();                

                $scope.close = function () {
                    $modalInstance.dismiss('cancel');
                }
            }]);
})();

The allSubsLoaded and userSubsLoaded objects are promises. To understand how those are resolved, here's a simplified version of my services

(function () {
    'use strict';

    angular.module('app.services.sharepoint')
        .factory('termStoreSvc', ['$log', '$q', function ($log, $q) {
            var self = undefined,
                deferred = $q.defer(),
                promise = deferred.promise;

            var termStoreSvc = function () {                
                self = this;

                self.init = init;
                self.ready = function (fn) {
                    return promise.then(fn);
                }
            };            

            function init() {
                $log.debug('Initializing');

                // do some stuff here                

                $log.debug('Initialized');
                deferred.resolve();
            };


            return termStoreSvc;
        }]);
})();

When I call init, it does some stuff and then resolves a hidden promise I declared so then when ready() is used with a callback function, the callback won't execute until the init() method is done.

For some reason, after I open/close it the first time and open it up a second/third/etc... time, the $q.all({array of promises}) is resolving early. Any ideas why?

Code Example for the solution:

The issue was that I didn't have the promise and deferred objects on the object I returned from the factory, which means that they were persisted (so after the first time they were still showing as resolved, causing my $q.all() to finish "early").

(function () {
    'use strict';

    angular.module('app.services.sharepoint')
        .factory('termStoreSvc', ['$log', '$q', function ($log, $q) {
            var self = undefined;

            var termStoreSvc = function () {                
                self = this;

                self.init = init;

                self.deferred = $q.defer();
                self.promise = self.deferred.promise;
                self.ready = function (fn) {
                    return self.promise.then(fn);
                }
            };            

            function init() {
                $log.debug('Initializing');

                // do some stuff here                

                $log.debug('Initialized');
                self.deferred.resolve();
            };


            return termStoreSvc;
        }]);
})();
Ben Black
  • 3,751
  • 2
  • 25
  • 43
  • At first glance it would seem that the promise in your factory needs to be inside the instance the factory returns for the promise be renewed each time you get an instance. Otherwise each instance you get from the factory would reference the same promise that is resolved the first time you open the window... I'll write an example when I get to my machine and post a fiddle. – hally9k Oct 01 '15 at 18:23
  • I understand exactly what you're saying and it makes sense. I'll give that a shot. – Ben Black Oct 01 '15 at 18:27
  • That's 100% what it was, if you post that as an answer I'll accept it. – Ben Black Oct 01 '15 at 18:39

2 Answers2

1

Well, the way I see it, factories are meant to return objects, that may have functions as attributes, and i've never done a new Instance of a factory (that new termStore thing). Is like you put a promise on the memory stack that has already been resolved the first time. Singleton pattern might be interfering with your intentions.

Janx from Venezuela
  • 1,147
  • 1
  • 10
  • 12
  • That's what it ended up being, I just needed to include the `deferred` and `promise` variables on the object returned instead of just inside the factory. I'm going to accept hally's answer since she posted that as a comment several minutes before you. Thanks for the direction though. – Ben Black Oct 01 '15 at 18:40
  • That's the fundamental difference between services and factories - factories are `new`able. – hally9k Oct 01 '15 at 18:55
1

An Angular factory is a singleton. Anything declared in the factory definition itself will be instantiated once for the life of the program. Anything declared in the object that the factory returns will, of course, be instantiated every time you new an instance from it. In your case a promise is declared in the factory definition and each instance the factory creates references that same promise. Hence your promise is already resolved for all instances apart from the first.

hally9k
  • 2,423
  • 2
  • 25
  • 47