Ember 2.2 relationships always null - ember.js

Can't seem to figure out why my relationships are always null.
app/models/group.js
export default Model.extend({
originalID: DS.attr('number'),
name: DS.attr('string'),
slideshows: DS.hasMany('slideshow', { async: true }),
});
app/models/slideshow.js
export default Model.extend({
originalID: DS.attr('number'),
title: DS.attr('string'),
group: DS.belongsTo('group', { async: true }),
});
Creating some data:
group = self.store.createRecord('group', {
originalID: 100,
name: 'Fake Group'
});
group.save();
slideshow = self.store.createRecord('slideshow', {
originalID: 101,
title: 'Fake Slideshow',
group: group
});
slideshow.save();
When I view the document in Pouch DB inspector group is always null. I'm following the guide on Ember's documentation page but it doesn't seem to work?

group.save() is an asynchronous operation, so you need to guarantee that it's finished before proceeding with the rest. Something like so should work:
group.save().then(g => {
let slideshow = this.store.createRecord('slideshow', {
originalID: 101,
title: 'Fake Slideshow',
group: g
});
slideshow.save();
});

Related

How can I add default values for ember has many relationship?

I'm using Ember 2.5.0 and I have two models service and availability which looks like:
// availability
import DS from 'ember-data';
export default DS.Model.extend({
day: DS.attr('string'),
enabled: DS.attr('boolean'),
startAt: DS.attr('string'),
endAt: DS.attr('string'),
service: DS.belongsTo('service')
});
And service which looks like:
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
availabilities: DS.hasMany('availability',
{
defaultValue:
[
{
day: 'saturday',
enabled: false,
startAt: '',
endAt: ''
},
{
day: 'sunday',
enabled: false,
startAt: '',
endAt: ''
}
]
}
)
});
As you can see I was trying to use defaultValue but with no luck. For new route I want to set default values if we are creating a new service record.
Any help is appreciated.
The argument hash that DS.hasMany only accepts two properties: async and inverse. It doesn't accept a defaultValue property though. (source).
But fear not, Eki Eqbal! I think you can accomplish something similar by using your model's ready() hook.
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
availabilities: DS.hasMany('availability', { async: true }), // is the data loaded async?
ready() { // fires when the model is loaded from server or created locally
if (!!this.get('availabilities')) {
// don't set to defaults if availabilities is not null
return;
}
var saturday = this.store.createRecord('availability', {
day : 'saturday',
enabled : false,
startAt : '',
endAt : ''
});
var sunday = this.store.createRecord('availability', {
day : 'sunday',
enabled : false,
startAt : '',
endAt : ''
});
this.get('availabilities').pushObjects([saturday, sunday]);
}
});

Duplicate records showing up in template

I have a survey app built with Ember JS and a Firebase backend with Emberfire adapter.
Here are the relevant portions of my user model:
//app/models/user.js
export default DS.Model.extend({
name: DS.attr(),
sessionuid: DS.attr(),
surveysToTake: DS.hasMany('survey', { async: true, inverse: 'respondents' }),
surveysCreated: DS.hasMany('survey', { async: true, inverse: 'creator' }),
responseSetsSubmitted: DS.hasMany('response-set', {async: true, inverse: 'respondentid'}),
respSetSurveyLookup: DS.attr({defaultValue: function() { return []; }})
});
And my responseSet (which is a collection of responses from a user)
//app/models/response-set.js
export default DS.Model.extend({
responses: DS.hasMany('response', { async: true, inverse: 'responseSet' }),
survey: DS.belongsTo('survey', { async: true, inverse: 'responseSets' }),
respondentid: DS.belongsTo('user', {async: true, inverse: 'responseSetsSubmitted'}),
});
And the survey model looks like this:
//app/models/survey.js
export default DS.Model.extend({
title: DS.attr(),
questions: DS.hasMany('question', { async: true, inverse: 'survey' }),
respondents: DS.hasMany('user', { async: true, inverse: 'surveysToTake'}),
responseSets: DS.hasMany('response-set', { async: true, inverse: 'survey' })
});
Now, when creating a user we do something like this ...
//routes/addusers.js (in the actions hook after getting properties from the controller)
var user = store.createRecord('user', {
id: userData.uid,
name: name
});
var responseSet = store.createRecord('response-set', {
survey: survey,
respondentid: user
});
user.setProperties({
'respSetSurveyLookup': [{'surveyId': _this.currentModel.get('id'),
'respSetId': responseSet.get('id')}],
'surveysToTake': [_this.currentModel]
});
_this.currentModel.get('respondents').pushObject(user);
Ember.RSVP.all([user.save(), _this.currentModel.save(), responseSet.save()])
.then(function(){
controller.setProperties({ multipleUsers: '', showAddUsers: false});
});
The user gets added as expected. However, in my template (which shows up on the same route as the 'add users' section,) the same record shows up multiple times.
Additional info:
I'm using Ember 1.13.11 with Ember Data 1.13.11 and EmberFire
1.6.3
Refreshing the page or reloading (with a 'location.reload()' ) causes
the user records to show up as expected.
This is possibly related to an Ember data issue which appears to be now closed and an upgrade of Ember data may resolve this in the future. However, is there anything I can do in the meantime to handle this issue ?

Saving Ember-Data records

Questions:
This line of code _activeAuthor.get('books').pushObject(book).save(); is processed without error in Chrome but the book is not added to the books property of the _activeAuthor instance of Ember-Data. I don't understand why?
The below code add the created Book to the Book property of the Chapter instance (see comment). It is a one-to-many relationship (see de Models). Ember-Data seems to automatically populate the related record on the Book instance. Is this a normal behaviour of Ember-Data? Should I let Ember-Data populate the related side of a relationship one-to-many or should I specify both sides and persist both instances?
I suspect that one of the issue of the below code is that the I do not handle promises properly. This code: this.modelFor('user').get('latestChapter'); seems to return a promise. How should I handle promisses with get()?
Code:
createChapter: function() {
//Getting the Author of the latestChapter or getting the first Author in the array
var _activeAuthor = null;
var authors = this.modelFor('user').get('authors').toArray();
var latestChapter = this.modelFor('user').get('latestChapter');
var latestAuthor = latestChapter.get('author');
if (latestChapter.content) {
_activeAuthor = latestAuthor;
} else {
_activeAuthor= authors[0];
}
var book = this.store.createRecord('book', {
title: 'click here to name your book',
author: _activeAuthor,
});
var chapter = this.store.createRecord('chapter', {
title: 'Click here to name your chapter',
book: book, // Add the created Book to the Book property of the Chapter instance
});
_activeAuthor.get('books').pushObject(book).save();
chapter.save();
book.save();
this.modelFor('user').set('latestChapter', chapter).save() //Identifying the latest created chapter at the lastestChapter;
console.log('New chapter created: ' + chapter.get('id'));
},
Models:
App.Author = DS.Model.extend({
type: DS.attr('string'),
authorTitle: DS.attr('string'),
userTitle: DS.attr('string'),
description: DS.attr('string'),
user: DS.belongsTo('user', {inverse: 'authors', async: true}),
books: DS.hasMany('book', { inverse: 'author', async: true}),
});
App.Book = DS.Model.extend({
title: DS.attr('string'),
icon: DS.attr('string'),
description: DS.attr('string'),
frequency: DS.attr('string'),
chapters: DS.hasMany('chapter', { inverse: 'book', async: true}),
author: DS.belongsTo('author', { inverse: 'books', async: true}),
});
App.Chapter = DS.Model.extend({
title: DS.attr('string'),
description: DS.attr('string'),
frequency: DS.attr('string'),
unit: DS.attr('string'),
aggregationMode: DS.attr('string'),
dashboard: DS.attr('boolean'),
statData : DS.attr('array'),
book: DS.belongsTo('book', { inverse: 'chapters', async: true}),
});
Thanks!
1.
author.get('books') will return a promise, so probably what you want to do is
author.get('books').then(function(books) {
books.pushObject(book)
});
author.save();
If this is not an issue, could you give a jsfiddle with the whole app code? Then, it'll be easier to help! :)
2.
Every time you get a model's property that is async and is not isLoaded (not synced with server), ember will ask the server and yes, will populate the records in your store which is a desired behaviour :)
3.
If you have an async model property, then you always get a promise, so you should handle it in for example this way:
chapter.get('book').then(function(book) {
// here's a book
});
BTW var latestAuthor = latestChapter.get('author'); -> chapter doesn't have author property :)

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')
})

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