Acces parent record from an embedded object - ember.js

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.

Related

Cannot delegate set('personName', s) to the 'content' property of object proxy <DS.PromiseObject:ember323>: its 'content' is undefined

I am trying my hands on ember js and I am not able to use multiple models within models, single models with string attributes are easy...
model/client.js
export default DS.Model.extend({
person: DS.belongsTo('person', {async: false, inverse: 'client'}),
postalAddress : DS.belongsTo('address', {async: false}) ,
residentialAddress : DS.belongsTo('address', {async: true}) ,
personName: DS.attr('string'), //added from person
greeting : DS.attr('string') //added from person
});
model/person.js
export default DS.Model.extend({
name : DS.attr('string'),
personName : DS.attr('string'),
greeting : DS.attr('string') ,
client: DS.belongsTo('client', {inverse: 'person'})
});
My Route
routes/client/create
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return Ember.RSVP.hash({
'client': this.get('store').createRecord('client' ,
{ //does this not initialize person
person: 'person', address: 'address'
} ),
// )
//does this not initialize person
'person': this.get('store').createRecord('person',{
client: 'client'
}),
'address': this.get('store').createRecord('address')
}
);
}
});
my template has only one line :
{{client-form client=model.client person=model.client.person address=model.client.residentialAddress errors=model.errors onSave=(action "save") }}
in my client-form.hbs i had some fields that referred to client.person.personName or client.person.greeting, and when i would type the first character I would get for greeting or personName
Assertion Failed: Cannot call set with 'greeting' on an undefined object.
So I added personName and greeting in client model directly as well and added two fields referring to client.personName and client.greeting and those fields dont give me any issue. I believe that the person model is not initialized and perhaps the client is instantiated but person and address is not and person is an undefined object.
The reason for such a model is that I have a java backend and my ClientDTO looks like:
public class ClientDTO /*implements IsSerializable*/ {
private PersonDTO personDTO;
private AddressDTO postalAddress;
private AddressDTO residentialAddress;
Where have i missed initializing the person and address, I can find ppl having errors "cannot call get on undefined" but not in set.
Edit:
Now after looking at some examples online I changed my Controller to initialize some models
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
save() {
console.log("yes controller");
let client = this.model.client;
let person= this.model.person;
let residentialAddress = this.model.client.residentialAddress;
client.save().then(() => {
person.save();
residentialAddress.save();
this.transitionToRoute('message', {queryParams: {message: 'created'}});
});
}
}
});
and changed my create route to
routes/client/create.js
export default Ember.Route.extend({
model() {
return Ember.RSVP.hash({
client: this.get('store').createRecord('client' ,
{
person: 'person', residentialAddress: 'address'
} ),
// )
party: this.get('store').createRecord('person',{
client: '
client'
}),
residentialAddress : this.get('store').createRecord('address')
}
);
}
});
Now I get:
Assertion Failed: Cannot delegate set('partyName', aaa) to the 'content' property of object proxy <DS.PromiseObject:ember323>: its 'content' is undefined.
Which is justr another way of saying the same thing. I think i am not creating the models in the route correctly.
You are creating the models fine but not associating them correctly.
'client': this.get('store').createRecord('client' , {
person: 'person', // Here you are setting client.person to the string 'person', not to the person model
address: 'address'
}),
What you want is more like:
let person = this.get('store').createRecord('person');
let address = this.get('store').createRecord('address');
return this.get('store').createRecord('client', { person: person, address: address );
// Or the same thing but use shorthand syntax to assign person and address
let person = this.get('store').createRecord('person');
let address = this.get('store').createRecord('address');
return this.get('store').createRecord('client', { person, address );
Then in your template just pass the client to your component
{{client-form client=model ...}}
And In your template use client.person etc.
If you still want to return a hash from the model hook you can do:
let person = this.get('store').createRecord('person');
let address = this.get('store').createRecord('address');
return Ember.RSVP.hash({
client: this.get('store').createRecord('client', { person: person, address: address ),
person: person,
address: address
});

Dynamic Properties on Model with EmberJS

Below is a basic ember model:
App.Person = DS.Model.extend({
firstName: DS.attr('string'),
birthday: DS.attr('date'),
day1: DS.attr('string'),
day2: DS.attr('string'),
day3: DS.attr('string')
});
For the purpose of this example, what if I had days go up to 50? Rather than going line by line... day4, day5, day6... is there a way to loop through dynamically? My first instinct is to use a MIXIN, and push these onto the object, but I don't think it would work if I had computed property:
isHoliday: function(){
if(this.get('day1') == 'off'){
return true;
}
}.property('day1'),
Given that 'this' is in there and we have a return, I don't believe you can simply 'push' this onto the model to generate something like this:
App.Person = DS.Model.extend({
firstName: DS.attr('string'),
birthday: DS.attr('date'),
day1: DS.attr('string'),
day2: DS.attr('string'),
day3: DS.attr('string'),
isHoliday: function(){
if(this.get('day1') == 'off'){
return true;
}
}.property('day1')
});
This is strange approach but still possible:
var defaults = {
firstName: DS.attr('string'),
birthday: DS.attr('date'),
isHoliday: function(){
if(this.get('day1') == 'off'){
return true;
}
}.property('day1')
}
var dayProps = {};
var count = 20;
while(count--){
dayProps['day' + (count + 1)] = DS.attr('string');
}
App.Person = DS.Model.extend(Ember.merge(defaults, dayProps));
instead defining with dynamic props, it is better to define Day model and have one-to-many relations with Person:
App.Person = DS.Model.extend({
days: DS.hasMany('day')
});
App.Day = DS.Model.extend({
person: DS.belongsTo('person')
});

ember data array UI not updating on pushObject

I have a list of product-tag that I fetch for my model.
Route:
model: function() {
return {
product_tags: this.store.find('product-tag', {merchant: merchId})
}
}
I have a component that adds tags to the model, however when after I create the record and push it into the model (as suggested on other posts) my UI still isn't updating.
addTag: function(name) {
tag = this.store.createRecord('product-tag', {
name: name
});
this.model.product_tags.toArray().addObject(tag);
tag.save();
}
//model merchant.js
export default DS.Model.extend({
user_id: DS.attr('number'),
product_tags: DS.hasMany('product-tag', {async: true})
});
//model product-tag.js
export default DS.Model.extend({
merchant: DS.belongsTo('merchant'),
name: DS.attr('string'),
});
What am I doing wrong? Thanks in advance.
You should make it array in the route, so u can use it always afterwards like u want. Your calling toArray() which makes a new Array instance, then your model is not hooked to the array u just made.
model: function() {
return {
product_tags: this.store.query('product-tag', {merchant: merchId}).then(function(pt) {
return pt.toArray();
});
}
}
var x = this.get('model.product_tags') === model's p_t // true
var y = this.get('model.product_tags').toArray() === model's p_t // false
Later on just do
addTag: function(name) {
this.get('store').createRecord('product-tag', {
name: name
}).save().then(function(saved){ 
this.get('model.product_tags').pushObject(saved);
}.bind(this);
}

Explicit reverses in Ember-Data

My model looks like this:
App.Room = DS.Model.extend({
title : DS.attr('string'),
description: DS.attr('string'),
rooms : DS.hasMany('room', {
async : true,
inverse: 'parent'
}),
parent : DS.belongsTo('room')
});
Viewing existing records works, except creating new records.
I tried it like this:
var self = this,
parent = this.get('content'),
input = this.getProperties('title', 'description'),
newRoom = this.store.createRecord('room', {
title : input.title,
description: input.description,
parent : parent
});
parent.get('rooms').then(function (rooms) {
rooms.pushObject(newRoom);
newRoom.save();
parent.save();
self.transitionToRoute('rooms');
});
But get this error:
Assertion Failed: You defined the 'parent' relationship on (subclass of DS.Model),
but multiple possible inverse relationships of type (subclass of DS.Model) were
found on (subclass of DS.Model).
You're relating an object to itself, so it's likely that Ember-Data's inverse guessing algorithm is getting confused. Declare the inverse on both explicitly.
App.Room = DS.Model.extend({
rooms: DS.hasMany('room', {
async: true,
inverse: 'parent'
}),
parent: DS.belongsTo('room', {
inverse: 'rooms'
})
});
Because it's specifically mentioning the parent relationship, I'm assuming Ember-Data is thinking that it might be its own inverse.

Ember.js belongsTo relationship undefined but hasMany array populated

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