Emberjs - Lazy load hasMany relationship - ember.js

I got two models set up like this:
App.Conversation = DS.Model.extend({
header : DS.attr('string'),
created_at : DS.attr('date'),
entry : DS.hasMany('Entry')
});
App.Entry = DS.Model.extend({
body : DS.attr('string'),
conversation: DS.belongsTo('Conversation'),
created_at : DS.attr('date'),
});
And routes like this:
App.ConversationsRoute = Em.Route.extend({
model: function() {
return this.store.find('conversation');
}
});
App.ConversationRoute = Em.Route.extend({
model: function(params) {
return this.store.find('conversation', params.id);
}
})
And a Conversation controller like this:
App.ConversationController = Em.ObjectController.extend({
actions: {
loadMore: function() {
}
},
entries: function() {
return this.get('entry');
}.property('model.entry')
});
Now, what I would like to do is only to supply 10 entries sideloaded with each Conversation. That works with no problems and I am able to retrieve all conversations with their hasMany relationship to Entry.
However I would like to lazyload more related entries at a later point by some functionality in my loadMore action, if the user clicks a button in my app.
How can this be achieved? I really can't wrap my head around how to load more entries and sync them to the Conversation model.
Thank you for your help.

Manually fetch the 10 records via find by query, then push them into the entry collection using pushObjects.
var entryCol = this.get('entry');
store.find('entry', {skip:10}).then(function (col){
entryCol.pushObjects(col);
});
Something like this (sorry phone)

Related

Bending Ember Data Records in Input Select

With this data structure:
App.Publisher = DS.Model.extend({
name: DS.attr('string'),
books: DS.hasMany('book', {async: true})
});
App.Book = DS.Model.extend({
title: DS.attr('string'),
summary: DS.attr('string'),
author: DS.belongsTo('author', {async: true}),
publisher: DS.belongsTo('publisher', {async: true})
};
In the book edit and book new forms, I'd like to display the publishers using a <input type=select>element.
My questions:
1 : how do I bind the ember-data publishers records to the input element.
2: how do I select the current book publisher in the element for edit or a default for a new book
3: how do I bind the selected publisher to the book, when submitting the form
Thanks a lot.
You can use Ember's built in select view for most of what you need here. To bind the list of publishers to the view's content you may need to get the list of publishers in the route. For example:
App.BookNewRoute = Ember.Route.extend({
var route = this;
var modelPromise = new Ember.RSVP.Promise(function(resolve, reject){
route.store.find('publisher').then(function(result){
console.log("got the publisher list");
// Have the new book model include the list of publishers, and a
// selectedPub that can be used as the initial selection in the
// drop down list, and will also be the model attribute bound to the list
var modelObj = {'publishers': result, 'selectedPub': result[0].get('id')};
resolve(modelObj);
});
return modelPromise;
});
Now in your template you can use 'publishers' with the select view:
{{view "select"
content=publishers
optionValuePath="content.id"
optionLabelPath="content.name"
value="selectedPub"}}
When the user submits the form, 'selectedPub' will have the id of the publisher for that book. Use that id to get the corresponding publisher model (e.g.):
bookController.find('publishers').filterBy('id', bookController.selectedPub)[0] )
Then set that model as the publisher for the book.
Here is the solution I found, which is super simple.
in the AppBooksNewController, I define a property called publishers which holds all my select elements.
AppBooksNewController = Ember.Controller.extend({
publishers: function () {
return this.store.findAll('publisher');
}.property(),
...
});
in my template books\new, I define the select view, using the property I've defined in the controller, and the property of the model I want to bind.
{{view "select"
content=publishers
optionValuePath="content"
optionLabelPath="content.name"
selectionBinding='model.publisher'
}}
That's all, The select is binded to the model.publisher.
and will be saved calling this.get('model').save() in the controller actions.
If it is a new record, I'm linking the default publisher during the new record creation:
App.BooksNewRoute = Ember.Route.extend({
model: function (params) {
var newBook = this.store.createRecord('book');
this.store.find('publisher', 1 ).then(function (publisher) {
newBook.set('publisher', publisher);
});
return newBook;
}
});

How to update model joined with belongsTo?

I have a Consultation model
export default DS.Model.extend({
records: DS.hasMany('record', { async: true }),
currentUser: DS.belongsTo('user'),
remoteUser: DS.belongsTo('user'),
created_time: DS.attr('number'),
freeMsgCount: function () {
return (this.get('remoteUser.msg_block')-this.get('user_msg_cnt'));
}.property('user_msg_cnt'),
.....
});
And User model
export default DS.Model.extend({
name: DS.attr('string'),
.....
});
And I try to update User model. I get json data via WebSocket
socket.on('message', function (jsonObj) {
if (jsonObj.action && jsonObj.action == 'userReload') {
self.store.push('user',jsonObj.userData );
return;
}
}
But Consultation model doesn't know about this update, because I have property freeMsgCount in Consultation model which is using data from User model this.get('remoteUser.msg_block'). User data was updated I saw it in Ember inspector. How can I tell Consultation model to update relation remoteUser?
It seems that you're not watching remoteUser changes in your computed property, so it won't be triggered if remoteUser property's changed. Please add remoteUser.msg_block to computed property declaration:
export default DS.Model.extend({
# ...
freeMsgCount: function () {
return (this.get('remoteUser.msg_block')-this.get('user_msg_cnt'));
}.property('user_msg_cnt', 'remoteUser.msg_block'),

EmberJS simple ID from related object

is there any way to do some filtering based on the related ID? imagine I have 2 models one is a song model and the other is an album model so obviously the song has album with an id, the json will return the model with
{album: 1}
so if I wanna filter it seems that I can't compare the album id with something like
song.get('album.id') === 1
is there any way to do it so simply?
Thanks
Edit: models
App.Album = DS.Model.extend({
irrelevantstuff: DS.attr(),
songs: DS.hasMany('Song')
})
App.Song = DS.Model.extend({
irrelevantstuff: DS.attr(),
album: DS.belongsTo('Album')
})
I tried both with {async:true} and without, still same problem.
however I noticed that if I run it the first time it gives me undefined to all of the album.id but if I run it the second time I get the ids, now I have everything on the store, so it's not doing an AJAX request either the first or second time.
{"songs": [{"irrelevantstuff":"foo", "album": 1}, {"irrelevantstuff":"bar", "album": 1}]}
{"albums": [{"irrelevantstuff":"album", "songs": [1,2]}]
Relationships should be camelCase
App.Album = DS.Model.extend({
irrelevantstuff: DS.attr(),
songs: DS.hasMany('song', {async: true})
})
App.Song = DS.Model.extend({
irrelevantstuff: DS.attr(),
album: DS.belongsTo('album', {async: true})
})
Since your data for the relationship isn't coming down in the request for the song/album it would be considered async. As such if you want to access it you'll need to use it asynchronous methods, promises.
var songPromise = this.store.find('song', 1);
songPromise.then(function(song){
var albumPromise = song.get('album');
albumPromise.then(function(album){
// the album is available for usage....
if(album.get('id') == 1){
alert('woo hoo');
}
});
});

ember-data creating a record with a one-to-many relationship

I'm trying to create a new record with a associated relationship from the next two models.
App.Kid = DS.Model.extend({
attribute1: DS.attr("string"),
parent: DS.belongsTo(parent)
});
App.Parent = DS.Model.extend({
attribute1: DS.attr("string"),
kids: DS.hasMany(kid)
});
And my route is the following. I'm using also an action handler in my template for save my model with the new values for the attributes through a form.
App.KidRoute = Ember.Route.extend({
model: function (id) {
return this.store.createRecord('kid', {parent: id});
},
actions:{
save: function(){
this.get('currentModel').save();
}
}
});
But I'm getting this error.
Assertion failed: You can only add a 'parent' record to this relationship
I know I'm doing something wrong but the thing is that it works if parent is just an attribute and not a belongTo relation. But I don't want this.
Thanks in advanced!
In that code:
this.store.createRecord('kid', {parent: id});
The variable id probally is some string, number etc. ember-data expects the model instance, so you will need to load it.
Try to use the following:
App.KidRoute = Ember.Route.extend({
model: function (id) {
var route = this;
return this.store.find('parent', id).then(function(parentModel) {
return route.store.createRecord('kid', {parent: parentModel});
});
},
actions:{
save: function(){
this.get('currentModel').save();
}
}
});

Ember-data mapping embedded object from JSON

I am struggling with a strange problem. I have a model called Activity with a property defined like this:
owner: DS.belongsTo('App.User', embedded: true)
The User is also a defined model when I'm getting the JSON response like this:
some single properties and
user: { id: etc. }
My all properties map well but the user embedded object from JSON doesn't map to the owner property. However, when I change
owner
to
user
It maps well. But I want to leave the owner because it's a better representation of what I mean. I tried this action:
owner: DS.belongsTo('App.User', key: 'user', embedded: true)
but it didn't help.
First, I recommend using the latest Ember / EmberData, but you will need to handle embedded records manually by enhancing extractSingle in a custom serializer (see example below). Also, you should define relationships like this:
App.Activity = DS.Model.extend({
name: DS.attr('string'),
owner: DS.belongsTo('user')
});
App.User = DS.Model.extend({
name: DS.attr('string'),
activities: DS.hasMany('activity')
});
Next, I recommend using the ActiveModelAdapter if you are using underscores when communicating with the server (i.e. like in EmberData 0.13):
App.ApplicationAdapter = DS.ActiveModelAdapter;
Finally, to use owner for a User, override typeForRoot in a custom serializer.
For example:
App.ApplicationSerializer = DS.ActiveModelSerializer.extend({
typeForRoot: function(root) {
if (root == 'owner' || root == 'owners') { root = 'user'; }
return this._super(root);
},
// based on: https://github.com/emberjs/data/blob/master/TRANSITION.md#embedded-records
extractSingle: function(store, type, payload, id, requestType) {
var owner = payload.activity.owner,
ownerId = owner.id;
payload.owners = [owner];
payload.activity.owner_id = ownerId;
return this._super.apply(this, arguments);
}
});
Example JSBin