Error for one hasMany but not another. Makes no sense - ember.js

I have a model "Group" that has some hasMany relationships "members" and "links". I have a controller that adds a new group but i get the following error on save
"Assertion Failed: A App.Group record was pushed into the store with the value of links being '{}', but links is a hasMany relationship so the value must be an array. You should probably check your data payload or serializer."
I can fix this by returning an empty links array in the response json but i don't necessarily want this included and i don't understand why i get this error for links but not for members as that isn't included in the response either.
App.AddGroupController = Ember.ObjectController.extend({
needs: ['application'],
actions: {
submitForm: function(){
var self = this;
var model = this.get('model');
self.send('showLoading', 'Saving');
model.save().then(function(){
self.send('hideLoading');
//Go to the groups list
self.transitionToRoute('groups');
},function(error){
self.send('hideLoading');
console.log(error);
//Must supply reject callback, otherwise Ember will throw a 'backend rejected the commit' error.
});
}
}
});
App.AddGroupRoute = App.AuthenticatedRoute.extend({
model: function(){
return this.store.createRecord('group');
}
});
App.Group = DS.Model.extend({
title: DS.attr('string'),
description: DS.attr('string'),
members: DS.hasMany('groupMember'),
links: DS.hasMany('groupLink'),
});
App.GroupLink = DS.Model.extend({
title: DS.attr('string'),
link: DS.attr('string'),
});
App.GroupMember = DS.Model.extend({
first_name: DS.attr('string'),
last_name: DS.attr('string'),
email: DS.attr('string'),
profile_picture: DS.attr('string'),
});
Response from save:
{
"group":{
"id":26,
"title":"Test Group",
"description":"Dummy group description",
}
}
Im using:
Ember : 1.9.1
Ember Data : 1.13.14

Related

Ember data: side-loading hasMany relationship using a non-id field

I was wondering if you can side-load a hasMany relationship in ember-data - hooked on a non-id column. Here are my code snippets-
App.Profile = DS.Model.extend({
firstName: DS.attr(),
lastName: DS.attr(),
photo: DS.hasMany('photo', {async:true})
});
App.Photo = DS.Model.extend({
path: DS.attr('string'),
title: DS.attr('string'),
owner: DS.belongsTo('user', {async:true}),
});
App.ProfileSerializer = DS.RESTSerializer.extend({
attrs:{
photo: {embedded: 'load'}
},
});
The JSON returned by localhost:/api/profiles/ is:
[
{
"photos": [
"media/pic3.jpeg",
"media/pic4.jpeg"
],
"id": "5441b6b2bc8ae304d4e6c10e",
"first_name": "Dave",
"last_name": "Gordon",
"profile_pic": "media/profilePic.jpg",
"member_since": "2014-01-03T00:00:00",
"membership": "Silver",
"theme_pic": "media/profilePic.jpg"
}
]
As we see here, I am trying to hook up photos using 'path' field of photo instead of id of photos. I can't seem to get ember to send an async call. Is it possible to tell ember to make an async call based off of an non-id field. I feel there should be a way coz I intend to send an async call based off of a custom generated key. ANy help is greatly appreciated. Thank You
I wouldn't think of that as an association, but rather just another property with a type of array.
You should be able to just change your model like this:
App.Profile = DS.Model.extend({
firstName: DS.attr(),
lastName: DS.attr(),
photos: DS.attr()
});
Then you should be able to access the property as an array (possible object) in your template.
If necessary, you might need to create a custom transform like this:
App.ArrayTransform = DS.Transform.extend({
deserialize:function(value) {
return value;
}
});
Then you can do:
App.Profile = DS.Model.extend({
firstName: DS.attr(),
lastName: DS.attr(),
photos: DS.attr('array')
});

Ember data stripping hasMany relationship

I am having a terrible time with this, but I have the following action on a controller:
saveOrganization: function() {
var org = this.get('model');
var users = org.get('users').then(function() {
console.log(org); //users are a part of org
org.save(); //users are not sent to server
});
}
and associated organization model:
export default DS.Model.extend({
createdAt: DS.attr('date'),
updatedAt: DS.attr('date'),
name: DS.attr('string'),
address: DS.attr('string'),
city: DS.attr('string'),
state: DS.attr('string'),
zip: DS.attr('string'),
users: DS.hasMany('user',{async:true}),
totalUsers: function() {
return this.get('users').get('length');
}.property('users.#each')
});
and associated users model:
export default DS.Model.extend({
createdAt: DS.attr('date'),
updatedAt: DS.attr('date'),
name: DS.attr('string'),
email: DS.attr('string'),
bio: DS.attr('string'),
picture: DS.attr('string'),
organization: DS.belongsTo('organization'),
type: DS.attr('string'),
username: DS.attr('string'),
password: DS.attr('string')
});
As you can see from the comments, when I get the users array as part of the organization, I can see the data, but when I save it, ember data never sends the users array as part of the data sent to the server.
Can anyone help me figure out what might be going on?
Thanks.
Stack over flow won't let me mark this as a dupe of Serialize ids when saving emberjs model, but it really is. Ember Data is stupid in this aspect, if it's a ManyToOne relationship, it only includes the id from the belongsTo side.
https://github.com/emberjs/data/commit/7f752ad15eb9b9454e3da3f4e0b8c487cdc70ff0#commitcomment-4923439
App.ApplicationSerializer = DS.RESTSerializer.extend({
serializeHasMany: function(record, json, relationship) {
var key = relationship.key;
var payloadKey = this.keyForRelationship ? this.keyForRelationship(key, "hasMany") : key;
var relationshipType = RelationshipChange.determineRelationshipType(record.constructor, relationship);
if (relationshipType === 'manyToNone' || relationshipType === 'manyToMany'
|| relationshipType === 'manyToOne') { // This is the change
json[payloadKey] = get(record, key).mapBy('id');
// TODO support for polymorphic manyToNone and manyToMany relationships
}
},
});

Value is showing up as [object Object] ember.js

I'm trying to set up the attributes to my model but having trouble when nesting objects. Here is what I have
App.Posts = DS.Model.extend({
title: DS.attr('string'),
author: {
name: DS.attr('string')
},
date: DS.attr('date'),
excerpt: DS.attr('string'),
body: DS.attr('string')
});
How am I suppose to declare the author object?
In the ember inspector when I go under data and under App.post and select one of the rows. It has a property a property App.Post with an attribute author: { name: [Object] }
here is the JS Bin link http://jsbin.com/tesozepexaqi/2/
Ember works perfectly fine without Ember Data. Let's pretend we want to do it with Ember Data:
Ember Data's Records should be flat. This means all properties are at the top level. If you have a related data that exist deeper they generally live in a different record. If you're attempting to embed the record you'll need to look into the tricky world of embedded records (Ember-data embedded records current state?). At the very least these related records must have an id defined. So here's an example of what the data returned from server should look like.
{
posts:[
{
id: '1',
title: 'My name is Django.',
author: {
id:1,
name: 'D name'
},
date: new Date('08-15-2014'),
excerpt: 'The D is silent.',
body: 'The D is silent.'
},
{
id: '2',
title: 'White horse',
author: {
id:2,
name: 'horse name'
},
date: new Date('08-15-2014'),
excerpt: 'Is what I ride.',
body: 'My horse likes to dance.'
}
]
}
Code
App.ApplicationAdapter = DS.RESTAdapter.extend();
App.PostSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
author: {embedded: 'always'}
}
});
App.Post = DS.Model.extend({
title: DS.attr('string'),
author: DS.belongsTo('author'),
date: DS.attr('date'),
excerpt: DS.attr('string'),
body: DS.attr('string')
});
App.Author = DS.Model.extend({
name: DS.attr()
});
Example: http://jsbin.com/vazada/1/edit
One other small tip, you'll not want to use globals when working with the routes, you can use modelFor to get the model from a different route.
App.PostsRoute = Ember.Route.extend({
model: function() {
return this.store.find('post');
}
});
App.PostRoute = Ember.Route.extend({
model: function(params) {
var posts = this.modelFor('posts');
return posts.findBy('id', params.post_id);
}
});
Personally, I think Ember Data is overkill. Ember works perfectly well with POJOs. If you need caching and the ability to rollback then Ember Data might be a good solution for you.
Example: http://jsbin.com/vazada/2/edit
Adjusting this example from the Ember docs like the below should work:
App.Post = DS.Model.extend({
title: DS.attr('string'),
author: DS.belongsTo('author'),
date: DS.attr('date'),
excerpt: DS.attr('string'),
body: DS.attr('string')
});
App.Author = DS.Model.extend({
post: DS.belongsTo('post')
})

Nesting resources error

I've followed this example http://emberjs.com/guides/controllers/dependencies-between-controllers/ to implement a nested resource for my app but continue to receive route and type errors.
I've created my route as follows:
App.Router.map(function () {
this.resource('logs', {path: '/'}, function(){
this.resource('log', {path:'/logs/:log_id'}, function(){
this.resource('triggers');
});
});
});
My controller:
App.TriggersController = Ember.ArrayController.extend({
needs:"log"
});
Model:
App.Log = DS.Model.extend({
name: DS.attr('string'),
type: DS.attr('string'),
messages: DS.attr('string'),
triggers: DS.hasMany(App.Trigger, {async:true})
});
Child Model:
App.Trigger = DS.Model.extend({
name: DS.attr('string'),
pattern: DS.attr('string'),
isEnabled: DS.attr('boolean'),
colour: DS.attr('string'),
highlightText: DS.attr('boolean'),
invertContrast: DS.attr('boolean')
});
JSFiddle link : http://jsfiddle.net/WZp9T/11/
Click on one of the links and you should see the error in console.
("Error while loading route: TypeError {}" and "Uncaught TypeError: Cannot read property 'typeKey' of undefined" as well as a deprecation warning)
Basically, what I'm trying to achieve is:
Logs -> Log -> Log Triggers -> Trigger
Each context should remain on screen, where exactly am I going wrong?
EDIT: It seems to be a problem with this:
App.LogIndexRoute = Ember.Route.extend({
model: function (params) {
return this.store.find(params.log_id);
}
});
If I remove that piece of code I no longer receive my errors.
You need to tell the store which kind of object you want to look up. Right now you're just passing it an ID. This is probably what you're looking for:
App.LogIndexRoute = Ember.Route.extend({
model: function (params) {
return this.store.find('log', params.log_id);
}
});

Failed to get child records without embedded property (JSBIN inside)

I would get the records of my field children. Here the code:
App.User = DS.Model.extend({
name: DS.attr('string'),
online: DS.attr('boolean')
});
App.List = DS.Model.extend({
name: DS.attr('string'),
children: DS.hasMany('App.User'),
online: function() {
var users = this.get("children");
return users.reduce(0, function(previousValue, user){ // no record founds
return previousValue + user.get("online");
});
}.property("children.#each.online")
});
But App.List.find(1).get('online') returns no record. (For some reason I cannot specify that App.List.children contains many records, of type App.Users, as embedded records).
Here is the fiddle: JSBIN and it's output
How I can solve my issue?
Define the embedded Model on your Adapter map:
App.List = DS.Model.extend({
name: DS.attr('string'),
users: DS.hasMany('App.User'), //use "users" as the property name to mantain ember's naming conventions
...
});
App.Adapter = DS.RESTAdapter.extend();
App.Adapter.map('App.List', {
users: {embedded: 'always'} //you can use `always` or `load` which is lazy loading.
});
App.Store = DS.Store.extend({
revision: 12,
adapter: App.Adapter.create()
});
Hope it helps