Ember.js belongsTo relationship undefined but hasMany array populated - ember.js

Using ember-data with Django REST adapter. Having trouble setting content of controllers based on the model received's relationships in setupController hooks
I put break points in and they went in this order:
ArtistRoute.model
ArtistTrackRoute.model
ArtistRoute.setupController
ArtistTrackRoute.setupController
Please see comments in those functions for more info / questions.
A track's primary artist is track -> belongsTo -> album -> belongsTo -> artist
Artists:
artist -> hasMany -> albums
Album
album -> belongsTo -> artist
album -> hasMany -> tracks
Track
track -> belongsTo -> album
I've checked my api output, and the resource link is definitely there.
Here's my models:
App.Album = DS.Model.extend({
'title' : DS.attr('string'),
'slug' : DS.attr('string'),
'year' : DS.attr('number'),
'type' : DS.attr('string'),
'image' : DS.attr('string'),
'genre' : DS.attr('string'),
'tracks' : DS.hasMany('App.Track'),
'artist' : DS.belongsTo('App.Artist')
});
App.Track = DS.Model.extend({
'title' : DS.attr('string'),
'slug' : DS.attr('string'),
'artists_titles' : DS.attr('string'),
'artists_ids' : DS.attr('string'),
'artists_slugs' : DS.attr('string'),
'year' : DS.attr('number'),
'genre' : DS.attr('string'),
'label' : DS.belongsTo('App.Label'),
'album' : DS.belongsTo('App.Album'),
'creator' : DS.attr('number'),
'created' : DS.attr('date'),
'modified' : DS.attr('date'),
});
App.Artist = DS.Model.extend({
'title' : DS.attr('string'),
'image' : DS.attr('string'),
'slug' : DS.attr('string'),
'genre' : DS.attr('string'),
'creator' : DS.attr('number'),
'created' : DS.attr('date'),
'modified' : DS.attr('date'),
'absoluteUrl' : DS.attr('string'),
'resourceUri' : DS.attr('string'),
'albums' : DS.hasMany('App.Album'),
'getImageURL' : function() {
return (this.get('image')) ? '/static/img/' + this.get('image') + '.jpg' : false;
}.property('image')
});
Here's my Routes:
App.ArtistRoute = Ember.Route.extend({
'model' : function(params) {
// here first
return App.Artist.find(params.artist_id);
},
'serialize' : function(model) {
"use strict";
return {
'artist_id' : model.get('id'),
'artist_slug' : model.get('slug')
};
},
'setupController' : function(controller, artist) {
// here third...
// I inspected artist._data
// artist._data.hasMany.albums = [1]
this.controllerFor('artist').set('content', artist);
}
});
App.ArtistTrackRoute = Ember.Route.extend({
'model' : function(params) {
// here second
return App.Track.find(params.track_id);
},
'serialize' : function(model) {
"use strict";
return {
'track_id' : model.get('id'),
'track_slug' : model.get('slug')
}
},
'setupController' : function(controller, track) {
// here fourth... I inspected the track data object
// track._data.belongsTo.album == undefined
// what I'm trying to achieve here is to set the album
// controller based on the belongsTo relationship
// this.controllerFor('album').set('content', track.get('album'))
this.controllerFor('track').set('content', track);
}
});
Moreover, when breakpointing in ArtistAlbumRoute.setupController, I noticed that album._data.hasMany.tracks == [1]
But album._data.belongsTo.artist == undefined.... wtf?!?!?!
I'm still having a hard time wrapping my head around Ember so any extra advice is much appreciated. Thanks!
Additionally, after the page has loaded and I open console, what would I type in to access the controllers to see what is set?

It turned out to have everything to do with my Tastypie Resource setup...
https://github.com/escalant3/ember-data-tastypie-adapter/issues/18
My TrackResource looked like this:
class TrackResource(ModelResource):
album = fields.ToOneField('samped.api.resources.AlbumResource', 'album')
class Meta:
queryset = Track.objects.all()
All I had to do was add "_id" to the album
class TrackResource(ModelResource):
album_id = fields.ToOneField('samped.api.resources.AlbumResource', 'album')
class Meta:
queryset = Track.objects.all()
Then I was able to do:
App.ArtistTrackRoute = Ember.Route.extend({
'model' : function(params) {
return App.Track.find(params.track_id);
},
'serialize' : function(model) {
"use strict";
return {
'track_id' : model.get('id'),
'track_slug' : model.get('slug')
}
},
'setupController' : function(controller, track) {
this.controllerFor('track').set('content', track);
this.controllerFor('album').set('content', track.get('album'))
}
});

Related

Modifying model in Ember

I'm putting together an app that displays a list of stores (with add/edit/delete options), and clicking on a store name takes you to the list of items in that store (again with add/edit/delete).
The model:
// app/models/shop.js
import DS from 'ember-data';
export default DS.Model.extend({
shopName: DS.attr('string'),
shopDetails: DS.attr('string'),
shopStock: DS.attr('array', {
defaultValue() {
return [];
}
})
});
Basically model should be as:
{
"shopName": "someName",
"shopDetails": "someDetails",
"shopStock": [
{
"name": "foo",
"description": "bar",
"price": "555"
}
]
}
For each shop the route is dynamical:
// app.router.js
Router.map(function() {
this.route('shop', function() {
this.route('stock', { path: '/:shop_id/stock' });
this.route('edit', { path: '/:shop_id/edit' });
});
});
And in the controller I have:
actions: {
saveItem() {
const newItem = {
name: this.get('itemName'),
description: this.get('itemDescription'),
price: this.get('itemPrice')
};
}
}
The question is, how do I push the newItem object into model's shopStock array?
Since you want to create/edit/save/delete the related child records, you should create a new model for the child (shopStock) that belongsTo the parent (shop).
// app/models/shop-stock.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
price: DS.attr('string'),
shop: DS.belongsTo('shop')
})
});
Your shop model should also have another field added, shopStocks: DS.hasMany('shop-stock').
When you want to add child records to the parent, you will use the .pushObject() method. See the Model Relationships section of the Guides for more details.

Ember howto use model field values in Controller

I am a newbie on Ember and break my head already a couple of hours how i can use the value of a model field in a controller ?
This is my model :
import DS from 'ember-data';
export default DS.Model.extend({
id_customer: DS.attr('number'),
id_default_group: DS.attr('number'),
id_lang: DS.attr('number'),
id_gender: DS.attr('number'),
active: DS.attr('boolean'),
email: DS.attr(),
firstname: DS.attr(),
lastname: DS.attr(),
company: DS.attr(),
birthday: DS.attr('date'),
date_add: DS.attr('date'),
date_upd: DS.attr('date'),
max_payment_days: DS.attr('number'),
newsletter: DS.attr('boolean'),
note: DS.attr(),
website: DS.attr()
});
This is my route :
import Ember from 'ember';
export default Ember.Route.extend({
beforeModel: function(){
if(!this.get('session.isAuthenticated')){
this.transitionTo('application');
}
},
model(params) {
return Ember.RSVP.hash({
customer: this.store.findRecord('customer', params.id),
address: this.store.query('address', {
orderBy: 'id_customer_fb',
equalTo: parseInt(params.id)
})
});
}
});
This is my controller :
import Ember from 'ember';
const genders = [
{ title: 'Dhr.', id_gender: '1' },
{ title: 'Mevr.', id_gender: '2' },
];
export default Ember.Controller.extend({
genders: genders,
selection: genders[1]**, <== THIS '1' MUST BE REPLACED WITH THE VALUE OF models.customer.id_gender ?????**
actions: {
chooseDestination(genders) {
this.set('selection', genders);
},
}
});
I would that the value 1 in this genders array could be the value of model.customer.id_gender ?
Yes you can.
Change selection like this :
selection: Ember.computed(function(){
let ret = this.get('genders').filterBy('id_gender', this.get('model.customer.id_gender'));
return ret.objectAt(0);
})
Please take a look at this twiddle

How to organize my model

i try to build a simple chat. User can select another one to talk with it. I use Ember with firebase. I've build my model like firebase example.
This is my simple model.
User Model :
import DS from "ember-data";
var user = DS.Model.extend({
name : DS.attr('string'),
messages : DS.hasMany("message", {async : true, inverse : 'owner'})
});
export default user;
Message Model :
import DS from "ember-data";
var message = DS.Model.extend({
date : DS.attr('date'),
content : DS.attr('string'),
owner : DS.belongsTo('user', {async : true}),
target: DS.belongsTo('user', {async : true})
});
export default message;
Emberfire doesn't support 'findQuery' ember-data type search, so how can i retrieve all messages that belong to a conversation? It is the right way to define my model or is there another one? In the ideal case, i would just want retrieve all message with a single request. ( from owner to target and from target to owner)
If you're sticking with the official emberfire bindings, you can set up three models:
User:
var user = DS.Model.extend({
name : DS.attr('string'),
conversations : DS.hasMany('conversation', { async: true }),
convos_users : DS.hasMany('convo_user', { embedded: true })
});
Conversation:
var conversation = DS.Model.extend({
messages : DS.hasMany('message', { embedded: true })
});
Message:
var message = DS.Model.extend({
date : DS.attr('date'),
content : DS.attr('string'),
from : DS.belongsTo('user', { async : true })
});
And then set up the embedded convos_users index:
var convos_users = DS.Model.extend({
with : DS.belongsTo('user', {async : true}),
conversation : DS.belongsTo('conversation', { async: true })
});
So the resulting schema looks something like this in firebase:
{
'users': {
'user_1': {
'name': 'Terrance',
'conversations': {
'convo_1': true
},
'convo_users': {
0: {
'with': 'user_2',
'conversation': 'convo_1'
},
...
}
},
'user_2': {
'name': 'Phillip',
'conversations': {
'convo_1': true
},
'convo_users': {
0: {
'with': 'user_1',
'conversation': 'convo_1'
},
...
}
},
...
},
'conversations': {
'convo_1': {
'messages': {
0: {
'date': 123456789,
'content': 'Hey buddy!',
'from': 'user_1'
},
1: {
'date': 123456789,
'content': 'Hey guy!',
'from': 'user_2'
},
...
}
}
}
}
This setup lets you embed messages together in a common conversation thread, so you only retrieve the messages for the conversation you want to see. The 'from' attribute in the message lets you render the user that it came from, and sort the alignment of the chat window, or whatever you're looking to do.
Finally, indexing both the list of conversations the user has ever been in, along with an index of the other user id in the conversation and that conversation's ID. This way, when user A goes to send a message to user B, you can do a computed findBy on the 'user_conversations' index. If a match exists, open the conversation with the conversation ID found, and append the messages to the conversation's embedded message array:
actions: {
sendMessage: function(msg) {
var userX = this.current_user.get('convos_users').findBy('with','user_X');
// No User
if (!userX) {
// 1. Create a new Conversation (var myRoom)
// 2. Save room id to users
// 3. Save room to your conversations model list
}
// Else
myRoom.messages.pushObject(msg);
myRoom.save();
}
}
}
Good luck!

Ember-data JSON structure

I have 5 models in some relations:
App.Service = DS.Model.extend({
name: DS.attr('string'),
service_prices: DS.hasMany('servicePrice')
});
App.ServicePrice = DS.Model.extend({
unit_price: DS.attr('number'),
qty_unit: DS.belongsTo('qtyUnit'),
service: DS.belongsTo('service'),
partner:DS.belongsTo('partner')
});
App.Partner = DS.Model.extend({
"name": DS.attr('string')
});
App.QtyUnit = DS.Model.extend(Ember.Validations.Mixin, {
name: DS.attr('string'),
});
App.Order = DS.Model.extend({
service: DS.belongsTo('service'),
unit_price: DS.attr('numeric'),
qty_unit:DS.belongsTo('qtyUnit')
});
I try to load an order with the following JSON:
var order = {
"order" : {"id":1,"service":1,"qty_unit":4,"unit_price":10},
"service":[{"id":1,"name":"ENG-GER","service_prices":[1,2]}],
"servicePrices":[
{"id":1,"qty_unit":4,"unit_price":3,"partner":1},
{"id":2,"qty_unit":5,"unit_price":4,"partner":1}
],
"qtyUnits":[
{"id":4,"name":"character"},
{"id":5,"name":"word"},
{"id":6,"name":"sentence"}
],
"partner":[
{"id":1,"name":"Jessie Bains"}
]
};
But im getting the following error:
Error while loading route: TypeError: Cannot read property 'deserialize' of undefined
Is my Json wrong structured?
Here is the JsBin:
http://jsbin.com/finahuna/12/edit
When requesting records, the relationships in the json should be plural (services, partners)
var order = {
"order" : {"id":1,"service":1,"qty_unit":4,"unit_price":10},
"services":[{"id":1,"name":"ENG-GER","service_prices":[1,2]}],
"servicePrices":[
{"id":1,"qty_unit":4,"unit_price":3,"partner":1},
{"id":2,"qty_unit":5,"unit_price":4,"partner":1}
],
"qtyUnits":[
{"id":4,"name":"character"},
{"id":5,"name":"word"}
],
"partners":[
{"id":1,"name":"Jessie Bains"}
]
};
Additionally your jsbin isn't working per say because:
services and qtyUnits didn't exist in the scope (possibly you debugging)
return Ember.RSVP.hash({
order:store.find('order',1),
services: store.all('service'),
qtyUnits: store.all('qtyUnit')
});
If your controller has an object backing it is needs to extend ObjectController not Controller
App.IndexController = Ember.ObjectController.extend({
});
Example: http://jsbin.com/wimoz/1/edit

Acces parent record from an embedded object

Is there a way to access to the parent object of an embedded model object ? For example :
App.Person = DS.Model.extend({
name : DS.attr('string'),
emails : DS.hasMany('App.Email', { embedded: true })
});
App.Email = DS.Model.extend({
label : DS.attr('string'),
email : DS.attr('string'),
setParentUpdated: function() {
if(this.get('isDirty') == true)
// this.get('parent').get('stateManager').goToState('updated');
// I would like to do something like this.get('parent')
// to access 'App.Person' instance object
}.observes('isDirty')
});
Why not simply setup a belongsTo relation?
App.Email = DS.Model.extend({
person: DS.belongsTo('App.Person')
//...
});
Then you will be able to use the person property of the email.