0

We have a project based on John Papa's HotTowel SPA Using Angular, Breeze and UI-Bootstrap.

We are running in to problems using clientside caching to load Drop downs from SQL Lookup tables. The view loads before the data is cached resulting in empty drop downs when the view first loads. If we refresh the view, the drops downs then populate. We realize that this is sequencing and routing issue but cannot figure out how to make it work.

The issue seems to center around the use of promises and how to recognize when they return successfully. We use the prime function to load the cached data from the database at startup but the search view is loaded before the data is accessible in the getActionDomain() function.

We would appreciate any pointers or ideas.

Thanks,

JG

app.run within app.js is the starting point

(function () {
    'use strict';

    var serviceId = 'app';
    var app = angular.module('app', [
        // Angular modules 
        'ngAnimate',        // animations
        'ngRoute',          // routing
        'ngSanitize',       // sanitizes html bindings (ex: sidebar.js)
        // Custom modules 
        'common',           // common functions, logger, spinner
        //'common.bootstrap', // bootstrap dialog wrapper functions

        // 3rd Party Modules
        'ui.bootstrap'      // ui-bootstrap (ex: carousel, pagination, dialog)
    ]);

    // Handle routing errors and success events
    app.run(['$route', '$rootScope', '$location', '$http', 'Auth', 'datacontext', 'common',  function ($route, $rootScope, $location, $http, Auth, datacontext, common) {

        var getLogFn = common.logger.getLogFn;
        var log = getLogFn(serviceId);
        var logError = getLogFn(serviceId, 'error');
        var logSuccess = getLogFn(serviceId, 'success');
        var $q = common.$q;

        //breeze.core.extendQ($rootScope, $q);
        primeData();

        function primeData() {

            return datacontext.prime()
                .then(startRouting)
                .then(querySucceeded, _queryFailed, null);


            function querySucceeded(data) {
                log('Retrieved [Lookups] from remote data source', data, true);
                return true;
            }



        }

        function startRouting() {
            $rootScope.$on('$routeChangeStart', function (event, next, current) {
                $rootScope.error = null;

                if ($rootScope.user) {
                    return true;
                } else {
                    $rootScope.user = {};

                    var defered = $q.defer();

                    checkRouting($q, $rootScope, $location);

                    return defered.promise;
                }

            });

            var checkRouting = function ($q, $rootScope, $location) {
                var defered = $q.defer();

                Auth.getCurrentUser()
                    .then(function (data) {
                        $rootScope.user.isInUserGroup = data.data.IsInUserGroup;
                        $rootScope.user.firstName = data.data.FirstName.replace(/\"/g, "");
                        $rootScope.user.lastName = data.data.LastName.replace(/\"/g, "");
                        $rootScope.user.userName = data.data.UserName.replace(/\"/g, "");
                    });

                return defered.promise;
            };
        }

        function _queryFailed(error) {
            var msg = config.appErrorPrefix + 'Error priming data.' + error.message;
            logError(msg, error);
            throw error;
        }
    }]);

})();

The prime function is found in the datacontext.js module:

function prime() {
    if (primePromise) return primePromise;

    var deferred = $q.defer();

    primePromise = $q.all([getLookupLists()])
        .then(extendMetadata)
        .then(success);

    function success() {
        setLookups();
        dataPrimed = true;
        //apps.startRouting();
        log('Primed the data');
    };

    function extendMetadata() {
        var metadataStore = manager.metadataStore;
        var types = metadataStore.getEntityTypes();
        types.forEach(function (type) {
            if (type instanceof breeze.EntityType) {
                set(type.shortName, type);
            }
        });

        function set(resourceName, entityName) {
            metadataStore.setEntityTypeForResourceName(resourceName, entityName);
        }
    }

    deferred.promise = primePromise;
    return deferred.promise;


}

function setLookups() {
    service.lookupCachedData = {
        actions: _getAllLocal('ActionDomain', 'sortorder'),
        statusCodes: _getAllLocal('StatusDomain', 'sortorder')
    }
}

function _getAllLocal(resource, ordering) {
    return EntityQuery.from(resource)
    .orderBy(ordering)
    .using(manager)
    .executeLocally();
}

function getLookupLists() {
    return EntityQuery.from('Lookups')
    .using(manager).execute()
    .then(querySucceeded, _queryFailed);

    function querySucceeded(data) {
        log('Retrieved [Lookups] from remote data source', data, false);
        return true;
    }

}

The code is called in the search.js view controller module

function activate() {
    common.activateController([getActionDomain(), getStatusDomain(), getCpuLog()], controllerId)
        .then(function () { log('Activated search View', null, false); });
}


function getActionDomain() {
    if (datacontext.lookupCachedData && datacontext.lookupCachedData.actions) {
        vm.actions.push({ value: 0 });
        datacontext.lookupCachedData.actions.forEach(function (actionItem) {
            vm.actions.push(actionItem);
        })
    }
}

function getStatusDomain() {
    if (datacontext.lookupCachedData && datacontext.lookupCachedData.statusCodes) {
        vm.statusList.push({ value: 0 });
        datacontext.lookupCachedData.statusCodes.forEach(function (statusItem) {
            vm.statusList.push(statusItem);
        })
    }
}
jwgreg
  • 133
  • 10

1 Answers1

0

If you want to wait that the promise is resolved before display the view you can use the resolve property in your service $routeProvider when you configure the app module.

There an example:

  $routeProvider.when('/staff_profil/edit', {
      templateUrl: 'app/app/assets/partials/profil-edit.html', 
       controller: 'ProfilEditController'
       resolve: {
          'currentUser':function( UserService){            
           return UserService.getCurrentUser();
      }
  }); 

You have to return a promise in the resolve!!

In this example, I wait to get the currentUser before display my profil-edit page. The UserService.getCurrentUser() is a promise create by the $http service in my UserService.

Moreover you can use this promise resolve in my controller ProfilEditController by injecte the 'currentUser' in my controller like if it is a service and then you can use currentUser.name in your controller and view.

I hope this will help you!

FlavienBert
  • 1,674
  • 3
  • 16
  • 17