Skip to content
Kent C. Dodds edited this page Jun 22, 2014 · 3 revisions

Table of Contents:

How Tos:

How Tos are solutions to common problems. They may not be the best for every circumstance (or any circumstance), so take them with a grain of salt.


How to: deal with child resources

Say you have two resources, users and posts. Let's also say that the server returns the post resource with a user property that's actually the user object. The problem is that now you have two resources loaded, but only one injected (the post). And if you are using the methods property when you defined your user resource, the post.user will not be of type User and you can't invoke any of the methods on that user.

Angular-data gives you the afterInject hook which can help to resolve this issue. The concept is pretty simple. After the post is injected, you tell DS to inject the user and replace the reference to the user in the post to the new injected user. You can do this like so:

DS.defineResource({
  name: 'user',
  endpoint: 'users',
  methods: {
    sayHi: function sayHi() {
      console.log('Hi! My name is ' + this.name);
    }
  }
});

DS.defineResource({
  name: 'post',
  endpoint: 'posts',
  afterInject: function afterInject(resourceName, attrs) {
    attrs.user = DS.inject('user', attrs.user);
  };
});

Now you can do post.user.sayHi() and everything will work splendidly.

If you have several resources with this same issue, you may consider implementing something more generic. Like this:

DS.defaults.afterInject = function defaultAfterInject(resourceName, attrs) {
  findAndInjectChildResources(attrs);
};

// All resources should be defined at this point
var resourceNames = Object.keys(DS.definitions);

function findAndInjectChildResources(value, key, parent) {
  if (angular.isObject(value)) {
    if (value.id && key) {
      if (_.contains(resourceNames, key)) { // using lodash
        parent[key] = DS.inject(key, parent[key]);
      } else if (angular.isArray(value) && _.contains(resourceNames, pluralize.singular(key))) { // using pluralize, to handle cases like "mouse" as a single resource or "mice" as an array of resources
        parent[key] = angular.forEach(value, function(val, index) {
          parent[key][index] = DS.inject(key, val);
        });
      }
    } else {
      value = angular.forEach(value, function(val, k) {
        value[k] = findAndInjectChildResources(val, k, value);
      });
    }
  }
  if (parent && key) {
    return parent[key];
  } else {
    return value;
  }
}

Note: if the service you're using is sending back author on the post instead of user, the generic solution above wont work and you may need to one-off that piece.

Clone this wiki locally