gkl
gkl

Reputation: 339

Angular Factory Variables Won't Update - Instance gets old values

I have a resource factory that builds objects for accessing our API. I use an environment variable to determine the base part of the URL - whether or not to include 'account/id' path segments when the admin user is 'engaging' a client account.

The sessionStorage item that holds the 'engagedAsId' doesn't get read, though for instances created after engaging an account. It requires a full reload of the app to pick up that change. Here is the factory code:

myapp.factory('ResourceSvcFactory',
    ['$rootScope', '$resource', 
    function ($rootScope, $resource) {

    function ResourceSvcFactory (endpoint) {
        // vvv problem is here vvv
        var accountId = sessionStorage.getItem('engagedAsId');
        var apiPath = (accountId != null) 
            ? '/api/account/' + accountId + endpoint 
            : '/api' + endpoint;

        var Resource = $resource(apiPath+':id/',{
            // default params
            id:''
        },{
            // custom actions 
            update: {method: 'PUT'}
        });

        return Resource;
    }

    return ResourceSvcFactory;
}]);

myapp.factory('AssetsResource', ['ResourceSvcFactory', function (ResourceSvcFactory) {

    var endpoint = '/assets/';
    var Resource = ResourceSvcFactory(endpoint);

    return Resource;
}]);

I implement this in my Controller like this:

myapp.controller('AssetGroupListCtrl', [ 'AssetgroupsResource', function (AssetgroupsResource) {

    var AssetGroups = AssetgroupsResource;

    // ... rest of controller
}]);

When i run this it works fine. But, if i change the engaged status in the sessionStorage without a full reload, the instance in the controller does not pick up the new path.

Is there a way to 'refresh' the instance? ...automatically?

Upvotes: 0

Views: 778

Answers (3)

gkl
gkl

Reputation: 339

After hours of research, it appears that the fundamental flaw in what I'm trying to do in the question is this: I'm trying to use a 'singleton' as a 'class'. from the docs:

Note: All services in Angular are singletons. That means that the injector uses each recipe at most once to create the object. The injector then caches the reference for all future needs. http://docs.angularjs.org/guide/providers

My work around was to create the $resource inside a method of a returned object. Here is an example: MyApp.factory('AssetgroupsResource', ['$rootScope', '$resource', function ($rootScope, $resource) {

    return {
        init: function () {
            var accountId = sessionStorage.getItem('engagedAsId');
            var apiPath = (accountId != null) 
                ? '/api/account/' + accountId + endpoint 
                : '/api' + endpoint;
                // default params
                id:''
            },{
                // custom actions 
            });
            return Resource;
        }
    }
}]);

This made it possible to build the object at the right time in the controller:

MyApp.controller('AssetGroupListCtrl', ['Assetgroups', function (Assetgroups) {
    var Assetgroups = AssetgroupsResource.init();
    // now I can use angular's $resource interface
}]);

Hope this helps someone. (or you'll tell me how this all could've been done in 3 lines!)

Upvotes: 2

Alex C
Alex C

Reputation: 1335

I think $resource uses promise which might be an issue depending on how you implement your factory in your controller.

$scope.$apply() can return an error if misused. A better way to make sure angular ticks is $rootScope.$$phase || $rootScope.$apply();.

Upvotes: 0

Shomz
Shomz

Reputation: 37701

You can always call $scope.$apply(); to force an angular tick.

See a nice tutorial here: http://jimhoskins.com/2012/12/17/angularjs-and-apply.html

Upvotes: 0

Related Questions