Currently developing an app using Ember-cli and having some dificulties with models with 2 words and relationships.
Model residents-profile
//models/residents-profile.js
DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
picture: DS.attr('string'),
phone: DS.attr('string'),
gender: DS.attr('string'),
residentsAccount: DS.belongsTo('residentsAccount')
}
** Model Residents-account **
//models/residents-account.js
DS.Model.extend({
email: DS.attr('string'),
password: DS.attr('string'),
profileId: DS.attr('number'),
residentsProfile: DS.belongsTo('residentsProfile', this.profileId),
});
the model hook on the residents route:
//routes/residents.js
model: function() {
return Ember.RSVP.hash({
residentsProfile: this.store.find('residentsProfile'),
residentsAccount: this.store.find('residentsAccount')
})
}
As soon as I try and fetch just the residents profile, i get an error "residents.index Cannot read property 'typeKey'"
But if I remove the relationship key from residents-profile and call only the residents profile the data is fetched correctly.
I'm using the RESTAdapter and a Restful API,
The models are being returned separately and the response from the server is as follows:
GET /residentsProfile
{
"residentsProfiles": [{
"id": 20,
"picture": null,
"phone": null,
"firstName": "Rocky",
"lastName": "Balboa",
"blockId": null,
"unitId": null,
"createdAt": "2014-09-17 19:54:28",
"updatedAt": "2014-09-17 19:54:28",
"residentsAccount": [5]
}]
}
GET /residentsAccount
{
"residentsAccounts": [{
"id": 5,
"email": "rocky#balboainc.me",
"admin": false,
"resident": true,
"profileId": 20,
"createdAt": "2014-09-17 19:54:29",
"updatedAt": "2014-09-17 19:54:29",
"residentsProfile": [20]
}]
}
EDIT
Besides the changes proposed by #Kingpin2k, which were spot on, i used setupcontroller in the following way:
setupController: function(controller,
controller.set('residentsAccount', models.residentsAccount);
controller.set('residentsProfile', models.residentsProfile);
}
Now everything works.
Three problems, both of your relationships should be async (since they aren't returned in the same response)
residentsAccount: DS.belongsTo('residentsAccount', {async: true})
residentsProfile: DS.belongsTo('residentsProfile', {async:true})
And both of the json response from them should be a single id, not an array
{
"residentsProfiles":[
{
"id":20,
"picture":null,
"phone":null,
"firstName":"Rocky",
"lastName":"Balboa",
"blockId":null,
"unitId":null,
"createdAt":"2014-09-17 19:54:28",
"updatedAt":"2014-09-17 19:54:28",
"residentsAccount": 5
}
]
}
Lastly, I'm not sure what you're trying to accomplish with this.profileId here, but it probably isn't doing what you think it is. this in that scope is probably the window, meaning you're passing in undefined likely.
Related
I'm sorry if this is a basic question, but since I'm quite new to ember, I'd like to know if there is any best practice for a case like this. For example, I have the follow endpoints that returns the payloads below:
https://api.example.com/v1/user
[
{
"user": "user1",
"firstName": "Foo1",
"lastName": "Bar1",
"url": "https://api.example.com/v1/user/user1"
},
{
"user": "user2",
"firstName": "Foo2",
"lastName": "Bar2",
"url": "https://api.example.com/v1/user/user2"
}
]
And each of the "url" endpoint returns something like this:
https://api.example.com/v1/user/user1
{
"user": "user1",
"firstName": "Foo1",
"lastName": "Bar1",
"age": 21,
"address": "User1 Address"
... more info ...
}
We see that some properties in "/user" are repeated in "/user/user1".
What would be the best practice to create the "user" model?
Should I have two models? Like for example a "users" model for the "/user" and a "user" model for "/user/user1"?
Could somehow have just one model "user" that would fit both endpoints?
Thanks in advance!
This is almost the use case described in the one-to-one docs where you're defining the user data with one model and linking another model with a belongsTo attribute:
// app/models/user.js
import DS from 'ember-data';
export default DS.Model.extend({
user: DS.attr('string'),
firstName: DS.attr('string'),
lastName: DS.attr('string'),
url: DS.attr('string'),
profile: DS.belongsTo('profile')
});
then setup a profile model with any extra values you're wanting to add and define the belongsTo attribute also:
// app/models/profile.js
import DS from 'ember-data';
export default DS.Model.extend({
age: DS.attr('string'),
address: DS.attr('string'),
user: DS.belongsTo('user')
});
In your routes file you'll want to setup the user id to define your URL structure like so:
//app/router.js
Router.map(function() {
this.route('users');
this.route('user', { path: '/user/:user_id' });
});
Then finally you'll need to load the data retrieving the related records and loading them in via your route file.
// app/routes/user.js
import Route from '#ember/routing/route';
export default Route.extend({
model(params) {
return this.store.findRecord('user', params.user_id, {include: 'profile'});
}
});
It's worth pointing out that you may also need a serializer to massage the data into the format you're wanting.
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')
});
I currently have a model with two hasMany relationships as seen below:
Ember Model:
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}),
templates: DS.hasMany('template',{async:true})
});
Using this model, I have new templates/controllers/etc setup to create new users and new templates. I can successfully save new/update existing organizations, but when I try to push objects into either users or templates, I lose what may already be in the other of the existing users or templates array.
I can see this is because my loading of the organization in the controller to create new records is not pulling in the other records, it pulls in it's own array, but not the other, for example this code on the new templates controller:
New Template Controller
organizations: function(){
return this.store.find('organization');
}.property(),
actions: {
createTemplate: function() {
var self = this;
console.log(self.get('organizations'));
}
}
Will show the templates in the console when attached to the record, but not the users. Vise versa, the same code on the new users controller does not show the templates it is pulling in but does show the users.
What can I do to get my find to return both arrays of relationships. They are both in the database, I can see them calling the rest api, but just not when they are found using ember.
I'm still pretty new with Ember, so any help would be greatly appreciated.
Thanks.
Edit
Sample JSON response from server
{
"organization": {
"id": 40,
"name": "Sample Business LLC",
"address": "1234 Fun Avenue",
"city": "Happytown",
"state": "NE",
"zip": "12345",
"users": [4,1],
"templates": [4,1],
"createdAt": "2014-02-18T23:10:08.466Z",
"updatedAt": "2014-02-18T23:10:08.466Z"}
},
"meta": {
"href": "http://server/organizations/40"
}
}
I'm having trouble loading some related models with ember-data. I'm pretty sure this is a syntax error on my part, but I just can't see it. Perhaps someone could be a second set of eyes for me?
The specific problem is that related models are not being displayed in my template.
Without further ado:
Notes
I'm using ember-appkit-rails.
This is my first ember project, so please forgive my n00bness.
I have a feeling that the answer may have something to do with embedded: 'always', but I have no idea how to integrate that into my code.
DEBUG: Ember : 1.4.0 ember.js?body=1:3462
DEBUG: Ember Data : 1.0.0-beta.7+canary.f482da04 ember.js?body=1:3462
DEBUG: Handlebars : 1.3.0 ember.js?body=1:3462
DEBUG: jQuery : 1.11.0 ember.js?body=1:3462
The problematic template
I'd think that this should work. It's almost right out of the ember-appkit-rails generators. The librettist and composer names are not appearing.
<h3>{{id}}</h3>
<p>{{link-to 'Edit' 'operas.edit' this}} <button {{action destroyRecord this}}>Destroy</button></p>
<ul>
<li>Title: {{title}}</li>
<li>Alternate Title: {{alternate_title}}</li>
<li>Source: {{source}}</li>
<li>Librettist: {{librettist.name}}</li>
<li>Composer: {{composer.name}}</li>
<li>Notes: {{notes}}</li>
</ul>
Models
opera.js.es6
export default DS.Model.extend({
title: DS.attr('string'),
alternateTitle: DS.attr('string'),
source: DS.attr('string'),
librettistId: DS.attr('number'),
composerId: DS.attr('number'),
notes: DS.attr('string'),
composer: DS.belongsTo('composer'),
librettist: DS.belongsTo('librettist')
});
librettist.js.es6
export default DS.Model.extend({
name: DS.attr('string'),
operas: DS.hasMany('opera')
});
composer.js.es6
export default DS.Model.extend({
name: DS.attr('string'),
operas: DS.hasMany('opera')
});
ActiveModel Serializer
class OperaSerializer < ActiveModel::Serializer
embed :ids, include: true
attributes :id, :title, :alternate_title, :source, :librettist_id, :composer_id, :notes
has_one :composer
has_one :librettist
end
Sample JSON being returned
This is what I see when I look at one opera record, but the structure holds for the entire set:
{
"composers": [
{
"id": 4097,
"name": "Müller, Wenzel"
}
],
"librettists": [
{
"id": 1414,
"name": "Bäuerle, Adolf"
}
],
"opera": {
"alternate_title": "oder Wien in einem anderen Weltteile",
"composer_id": 4097,
"id": 4166,
"librettist_id": 1414,
"notes": "these are some notes",
"source": "F116.Theater.a.d.Wien.260A.Mus; Mus.Hs.78.Mus; Mus.Hs.25409.Mus",
"title": "Aline Königin von Golkonda"
}
}
I've also tried this approach with inline embedding ({opera: {...composer: {...}}}) but that didn't work any better.
Thanks for your help!
Paul.
the _id are not necessary if you're using the RESTAdapter as your client-side adapter.
App.Opera = DS.Model.extend({
title: DS.attr('string'),
alternateTitle: DS.attr('string'),
source: DS.attr('string'),
notes: DS.attr('string'),
composer: DS.belongsTo('composer'),
librettist: DS.belongsTo('librettist')
});
App.Librettist = DS.Model.extend({
name: DS.attr('string'),
operas: DS.hasMany('opera')
});
App.Composer = DS.Model.extend({
name: DS.attr('string'),
operas: DS.hasMany('opera')
});
JSON
{
"composers": [
{
"id": 4097,
"name": "Müller, Wenzel"
}
],
"librettists": [
{
"id": 1414,
"name": "Bäuerle, Adolf"
}
],
"opera": {
"alternate_title": "oder Wien in einem anderen Weltteile",
"composer": 4097,
"id": 4166,
"librettist": 1414,
"notes": "these are some notes",
"source": "F116.Theater.a.d.Wien.260A.Mus; Mus.Hs.78.Mus; Mus.Hs.25409.Mus",
"title": "Aline Königin von Golkonda"
}
}
http://emberjs.jsbin.com/OxIDiVU/233/edit
If you're using the ActiveModelAdapter then you'd use the _id in the json, here's an example with the ActiveModelAdapter
http://emberjs.jsbin.com/OxIDiVU/234/edit
The example I have found online show something as follows:
Flashcards.Subject = DS.Model.extend({
name: DS.attr('string'),
flashGroups: DS.hasMany('flashGroup', {async: true})
});
Flashcards.FlashGroup = DS.Model.extend({
name: DS.attr('string'),
subject: DS.belongsTo('subject')
});
And then format JSON as follows:
{"subject": {"id": 1, "name": "foo", "links": {"flash_groups": "/subjects/1/flash_groups"}}}
When I try sumSubject.get('flashGroups'), nothing happens.
I believe there's a bug where links keys are not camelized. Try to return the following JSON:
{"subject": {"id": 1, "name": "foo", "links": {"flashGroups": "/subjects/1/flash_groups"}}}
(use flashGroups instead of flash_groups)