Question

I'm using Angular service with a Revealing Module pattern. The service internally pulls string resources from a web service and makes them available via the 'Strings' public variable. I have to initialize the private 'strings' variable because it is referenced before the service call is made.

I get the proper string data back from the service and copy it to the private 'strings' variable. However, when the client references the public 'Strings', it is still retaining its original value.

Any idea what I am doing wrong, or how to get public 'Strings' to update?

'use strict';

io1App.factory('Resources', ['$rootScope', 'DataService', '$q',
function ($rootScope, DataService, $q) {

   var urlBase = '/api/sfc/resource';

    // Need to pre-define 'ERROR_HEADER', since it is referenced in Index.html...before we have a chance to download it from the server.
   var strings = {
       'ERROR_HEADER': 'Error!'
   };

    var getStringResources = function (locale) {

        var url = urlBase + '/' + locale;

        var deferred = $q.defer();

        var promise = DataService.GetMethod(url);

        // Note that DataService.GetMethod(...) is returning a $q promise
        promise.then(function (data) {
            strings = data;
            deferred.resolve();
        },
        function (err) {
            deferred.reject(err);
        });

        return deferred.promise;
    };

    return {
        Strings: strings,
        GetStringResources: getStringResources
    }
}]);

The service call sets the returned data to the private 'strings' variable

promise.then(function (data) {
            strings = data;
            deferred.resolve();
        },

Private 'strings' is now showing the following (via Chrome Developer tools):

strings = {
       'ERROR_HEADER': 'Error!'
       'INVALID_PROCESS_ORDER': 'Process Order [%d] could not be entered because it does not belong to Manfacturing Order [%d]',
       'DUPLICATE_PROCESS_ORDER': 'Process Order [%d] already entered.',
       'USER_NOT_ITAR': 'Manufacturing order [%d] is ITAR, and you are not ITAR approved.  You cannot proceed with this order.  Please contact your supervisor.'
   };

Yet when is reference public 'Strings' in the Angular controllers, 'Strings' is still referencing the original value of private 'strings'.

Resource.Strings:

{
   'ERROR_HEADER': 'Error!'
};

Any suggestions?

Was it helpful?

Solution

You're replacing the strings variable completely with data. They are different objects and have different references, so any client code that has a reference to the previous strings object will not see the change.

Instead of replacing, you could augment the existing strings object.

promise.then(function (data) {
    // Merge properties into `strings`
    // You could also use any other "extension" technique, e.g. _.extend()
    for (var k in data) {
        if (data.hasOwnProperty(k)) {
            strings[k] = data[k];
        }
    }
    deferred.resolve();
},

OTHER TIPS

The JavaScript object you return from your factory method is never updated with the new strings object. You need to retain a reference to it to be able to update the "public view":

service = {
    Strings: strings,
    GetStringResources: getStringResources
};
return service;

Then update your .then callback to update both the private variable as well as the public service object:

.then(function(data) {
    strings = service.strings = data;
    deferred.resolve(data); // don't need to resolve this with the strings, but why not?
}, function error(){...});

However, since you no longer really have "private data" (only a private reference to the variable which is public), you don't really need to have the private strings variable at all:

io1App.factory('Resources', ['$rootScope', 'DataService', '$q',
function ($rootScope, DataService, $q) {

    var urlBase = '/api/sfc/resource';

    var service = {
        // Need to pre-define 'ERROR_HEADER', since it is referenced in Index.html...before we have a chance to download it from the server.
        Strings: { 'ERROR_HEADER': 'Error!' },
        GetStringResources: function (locale) {
            var url = urlBase + '/' + locale;
            var deferred = $q.defer();
            var promise = DataService.GetMethod(url);

            // Note that DataService.GetMethod(...) is returning a $q promise
            promise.then(function (data) {
                service.strings = data;
                deferred.resolve(data); // don't need to resolve this with the strings, but why not?
            },
        function (err) {
            deferred.reject(err);
        });

        return deferred.promise;
    };
    return service;
}]);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top