Search code examples
javascriptangularjsjsdatajs-data-angular

JS-Data error: attrs must contain the property specified by idAttribute - with hasMany relations


Here's the error: http://puu.sh/lXzja/f773fb6c9a.png

The primary key for my user model is username. The primary key for my routes is the routename. My api returns jsons inside data:{} as per jsonapi.org specs. Thus the id attribute is not in the top-level, as js-data demands. This is why I return data.data in the afterFind for 'users'. I tried to do something like that in 'routes' but it's an array of routes. The console log in beforeInject gives me:

result in beforeInject

Here's the config:

  DS.defineResource({
    name: 'users',
    idAttribute: 'username',
    basePath: apiEndpoint,

    relations: {
        hasMany: {
            routes: {
                localField: 'routes',
                foreignKey: 'username'
            }
        }
    },
    // set just for this resource
    afterFind: function(resource, data, cb) {
        // do something more specific to "users"
        cb(null, data.data);
    }
});

DS.defineResource({
    name: 'routes',
    idAttribute: 'routename',
    basePath: apiEndpoint,
    cacheResponse: true,
    relations: {
        belongsTo: {
            users: {
                parent: true,
                localKey: 'username',
                localField: 'users'
            }
        }
    },
    beforeInject: function(resource, data) {
        // do something more specific to "users"
        console.log(data);
        return data.data.routes;
    }
});

Here's where I try to load my routes but get that err:

  resolve: {
            user: function($route, DS) {
                var username = $route.current.params.username;
                return DS.find('users', username).then(function(user) {
                    DS.loadRelations('users', user.username, ['routes']).then(function(user) {
                        console.log(user);
                    }, function(err) {
                        console.log(err);
                    });
                });
            }
        }

Solution

  • Not only is your data nested under a "data" field, but a "routes" field. So when you find the routes, you're trying to inject something like:

    {
      routes: [{
        // foreignKey to a user
        username: 'john1337',
        // primary key of a route
        id: 1234
      }]
    }
    

    when you need to be injecting:

    [{
      username: 'john1337',
      id: 1
    }]
    

    Add an afterFindAll on your routes resource to cb(null, data.data.routes).

    You'll either need to:

    A) Add lots of "after" hooks to all your Resources or

    B) Make the deserialization generic so it works for all Resources. Perhaps something like this?

    DS.defaults.afterFind = function (Resource, data, cb) {
      cb(null, data.data[Resource.name])
    };
    DS.defaults.afterFindAll = function (Resource, data, cb) {
      cb(null, data.data[Resource.name])
    };