14
votes

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?

4
$cacheFactory is probably the answer, but you'll probably have to look at the Angular source code to figure out what cacheId they use. Also/instead, can't you just set your $scope property to the JSON object (or do you wrap $resource in a service that alters the data so that it is "fit" for $scope consumption)?Mark Rajcok
I need the $resource actions, like update, so can't use the raw JSON object on the scope.Kris Braun
Okay, so looking at @Brandon's answer, $cacheFactory is not the answer for $resource. ($cacheFactory might be useful if you switch to $http though, if you someday find $resource too limiting.)Mark Rajcok

4 Answers

6
votes

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.)

1
votes

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)
  ]
1
votes

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, ...},
...}
1
votes

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.