Initializing AngularJS $resource with in-page JSON

All we need is an easy explanation of the problem, so here it is.

I’m using AngularJS’ $resource to fetch and update an object. To save a round trip on page load, I have the JSON object in a variable on the page. How can I initialize the $resource with this data rather than calling $get?

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

You can use new to create instances of resources created with $resource:

window.somePreloadedJson = {
  id: 1,
  name: 'lancelot',
  quest: 'holy grail',
  color: 'blue',
};

app.factory('myResource', function($resource) {
  return $resource('/my/fake/:id', { id: '@id' });
});

...

$scope.resource = new myResource(window.somePreloadedJson);

Here is an example of this technique on jsFiddle, which demonstrates that $save, $delete, etc. work as expected on the created instance. (Some debugging code has been added to log out HTTP requests rather than actually make them, so you can see what requests would have been made in the page.)

Method 2

It is blindingly frustrating to me that this is not better answered. Here is my solution. I don’t know if it qualifies as lame or not, but it works for me:

# Loading json data from the document into angular seems to be a pain.
# The sole purpose of this controller is to make that easier.

# Let's say you want to load up the following JSON: [{"name": "Joe", "id": 1}]
# AND you have an angularjs service called People with a list
# You would do the following:

# <any_element ng-controller='JsonDataController' data-json='[{"name": "Joe", "id": 1}]' data-service='People' data-binding='list'></any_element>
# The controller would then set People.list = JSON.parse($attrs.json)

# And that's all there is to it.

window.JsonDataController = ['$scope', '$attrs', '$injector', ($scope, $attrs, $injector) ->
  service = $injector.get($attrs.service)
  attributeName = $attrs.binding
  service[attributeName] = JSON.parse($attrs.json)
  ]

Method 3

You can use a custom $cacheFactory to initialize the $resource with the data.

Let’s say you have:

window.initData = {
    '/request1': 'a',
    '/request2': [1, 2, 3],
    '/request3': {c: 'c', d: 'd'}
};

First, create the custom cache factory:

angular.module('app').factory('PreloadedCache', function($cacheFactory, $window) {
    var PreloadedCache = $cacheFactory('preloadedCache');

    // Pre-populating the $resource cache
    angular.forEach($window.initData, function(item, key) {
        PreloadedCache.put(key, item);
    });

    // Overwrite the put cache function - prevent saving in two places
    PreloadedCache.put = function() {};

    return PreloadedCache;
});

Then, declare a cache property on the resource action (from the docs):

cache – {boolean|Cache} – If true, a default $http cache will be used to cache the GET request, otherwise if a cache instance built with $cacheFactory, this cache will be used for caching.

{action1: {method:?, params:?, cache: PreloadedCache, ...},
action2: {method:?, params:?, isArray:?, headers:?, cache: PreloadedCache, ...},
...}

Method 4

You can use the angular-mocks.js script, available here. This script allows you to intercept service calls and substitute responses at runtime, very useful for testing purposes.

For example, given a service:

app.factory('MyService', ['$resource', function($resource) {
    return $resource('http://myservice/service', {}, {
        get: {
            method:'GET', 
            params:{}, 
            isArray:false
        }
    });
}]);

it can be redirected this way:

app.config(function($provide) {
    $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
});

app.run(function($httpBackend) {
    $httpBackend.whenGET(new RegExp(".*/myservice/service.*")).respond(200, mockData.fake);
    ...
}

where mock data is defined somewhere like this:

var mockData = new Object();
mockData.fake = ...

To activate the fake service just include the angular-mocks script and the above mentioned definitions, to switch to regular one just comment out or remove them.

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply