Build dynamic relationship in ember data - ember.js

I have access to API endpoints to only get, cannot suggest or apply any modifications.
This a sample schema of each api:
var person = DS.Model.extend {
firstName: DS.attr(),
lastName: DS.attr()
}
var credit = DS.Model.extend {
name: DS.attr(),
personId: : DS.belongsTo('person')
}
var debit = DS.Model.extend {
name: DS.attr(),
personId: : DS.belongsTo('person')
}
I can get api/person, api/credit, api/debit individually. After fetching the data. I want to map relationship between person and credit/debit similar to this...
var person = DS.Model.extend {
firstName: DS.attr(),
lastName: DS.attr(),
**debits: DS.hasMany('debit'),**
**credits: DS.hasMany('credit')**
}
How can I accomplish this in ember-data?
JSbin - http://emberjs.jsbin.com/gewowucamo/8/edit?html,js,output

Everything you're doing will work. When you load the data using store.push() it builds the related relationships for you (if they exist).
This JSBin gives you an example of what you're doing that works.
I believe the problem you have is likely related to using Fixtures - and at a guess I would assume it doesn't follow the same behaviour/logic when loading data.
Fixtures are being removed from Ember (see this PR)

You have already answered your own questions, as the code you have given should already work.

Related

Ember data: side-loading hasMany relationship using a non-id field

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

Ember Data loading hasMany relation doesn't work in Ember App Kit (EAK)

I'm trying to set up a hasMany relationship between two models and a hasOne (belongsTo in the current version of Ember Data) between the hasMany and hasOne.
I'm working with Ember Data and have a made a RESTful API that works according to Ember's conventions. All the classes can be queried individually.
Bookmark = hasMany -> Termbinding
Termbinding = belongsTo -> Term
Term = belongsTo -> Termbinding
So the goal is to fetch a Bookmark and get the Terms that are attached to it through the Termbinding. I would already be pretty happy to get the Bookmark to Termbinding relation working. I went through all questions posted on here, sadly enough that didn't work.
Router.js
var Router = Ember.Router.extend();
Router.map(function() {
this.resource('bookmarks', { path:'bookmarks'});
this.resource('bookmark', { path:'bookmarks/:bookmark_id' });
this.resource('termbindings', { path:'termbindings' });
this.resource('termbinding', { path:'termbindings/:termbinding_id' });
});
export default Router;
Bookmark.js
var Bookmark = DS.Model.extend({
url: DS.attr('string'),
description: DS.attr('string'),
visits: DS.attr('number'),
termbinding: DS.hasMany('termbinding')
});
export default Bookmark;
Termbinding.js
var Termbinding = DS.Model.extend({
bookmarkId: DS.attr('number'),
termId: DS.attr('number'),
termOrder: DS.attr('number'),
bookmarks: DS.belongsTo('bookmark')
});
export default Termbinding;
I hope someone can help me because this is preventing me from using Ember for my bookmark application. Thanks in advance.
It might be wise to explicitly specify your inverses, i.e.
var Termbinding = DS.Model.extend({
bookmarkId: DS.attr('number'),
termId: DS.attr('number'),
termOrder: DS.attr('number'),
bookmarks: DS.belongsTo('bookmark', { inverse: 'termbinding' })
});
export default Termbinding;
var Bookmark = DS.Model.extend({
url: DS.attr('string'),
description: DS.attr('string'),
visits: DS.attr('number'),
termbinding: DS.hasMany('termbinding', { inverse: 'bookmarks' })
});
export default Bookmark;
Ember Data will try to map inverses for you, however, it is not without faults. It could possibly be that your pluralization of 'bookmarks' on a DS.belongsTo relationship is throwing off its automatic inverse mapping. Typically for belongsTo you would use the singular, 'bookmark'. Conversely, your hasMany would be termbindings: DS.hasMany('termbinding')
Also, if you could show where you're invoking the models that would be greatly appreciated. Typically I find that creating a JSbin at emberjs.jsbin.com helps me isolate the problem and also provides a collaborative space to debug and experiment.

Saving a model, that has many services, where are my ids

I have the following model
MyApp.Company = MyApp.User.extend
companyName: DS.attr('string')
companyEmail: DS.attr('string')
cvr: DS.attr('number')
services: DS.hasMany('service')
Which has many services
MyApp.Service = DS.Model.extend
name: DS.attr('string')
description: DS.attr('string')
content: DS.attr('string')
avatar: DS.attr('string')
smallAvatar: DS.attr('string')
serviceField: DS.belongsTo('serviceField')
companies: DS.hasMany('company')
Now i have a panel, where the user adds services to his company, and end up pushing the save button which triggers this action in my controller.
saveAccount: ->
#get('model').save()
Now my problem is that, with the new services added, i would expect the data in the put request to look something like this.
company: {
company_email: "fsfsdfsdf"
company_name: "Elvar's biks"
cvr: 389298498
services: [1,4,2]
}
But the "services" is missing; am i expecting a wrong json structure? If so, how should i tackle this?
Might be worth noting, that all the object are created before hand, so the client i choosing from existent services, i just need to create the relations server side.
Looks like you are trying to set up a many to many relationship, so you'll want to use a bridge table/model here.
First, create a new model called CompanyService:
MyApp.CompanyService = DS.Model.extend
company: DS.belongsTo 'company'
service: DS.belongsTo 'service'
Next, update your company model:
MyApp.Company = MyApp.User.extend
companyName: DS.attr('string')
companyEmail: DS.attr('string')
cvr: DS.attr('number')
companyServices: DS.hasMany('companyService', {async:true})
Then, update your service model:
MyApp.Service = DS.Model.extend
name: DS.attr('string')
description: DS.attr('string')
content: DS.attr('string')
avatar: DS.attr('string')
smallAvatar: DS.attr('string')
serviceField: DS.belongsTo('serviceField')
companyServices: DS.hasMany('companyService', {async:true})
From here you'll need to modify whatever backend setup (Rails?) you have going to accommodate this new model relationship.
Hope that helps!

Ember Data saving

I'm having a ton of trouble with updating a model in my Ember application. I can't seem to find good documentation that describes how to update my app. The following code is what I'm trying. This sends an update to /playlists/:playlist_id, unfortunately it doesn't send the updated songs as well... is there some callback for pushObject that I can't find? Am I trying trying to save the wrong thing?
App.PlaylistIndexController = Ember.ObjectController.extend({
actions: {
addSong: function(song) {
songs = this.get('songs');
songs.pushObject(song);
this.get('model').save();
}
}
});
App.Playlist = DS.Model.extend({
name: DS.attr('string'),
songs: DS.hasMany('song'),
});
App.Song = DS.Model.extend({
name: DS.attr('string'),
artist: DS.attr('string'),
playlist: DS.belongsTo('playlist'),
});

Best way to namespace multiple or suit of emberjs apps

First a Merry Xmas to you and thanks for helping with suggestions.
My question is still on emberjs namespace but this time in the context of a suite of multiple emberjs apps which will be contained within multiple rails-engine, so that each emberjs app is a standalone app with its own controllers, models, views and routers. However, they will still need to share ember-data associations. These rails-engines will inturn be included in the main-rails app where each engine represents a major feature of the app.
In this jsfiddle, I came up with 3 approach to namespace, but I will like to know which one is the emberjs way:
**Approach 1**
//Each emberjs app with its own namespace
MainRailsApp = Ember.Application.create();
RailsEngine = Ember.Namespace.create();
RailsEngine2 = Ember.Namespace.create();
MainRailsApp.Store= DS.Store.extend(); **inherits from Ember.Application**
MainRailsApp.Router = Em.Router.extend() **inherits from Ember.Application**
console.log(RailsEngine.toString()); //RailsEngine
console.log(RailsEngine2.toString()); //RailsEngine2
RailsEngine.Model = DS.Model.extend
RailsEngine2.model = DS.Model.extend
Can this model's share association though they inherit from different namespace
Contact.Model = RailsEngine.Model.extend({
address: DS.attr('string'),
user: DS.belongsTo('User.Model')
});
User.Model = RailsEngine2.Model.extend({
name: DS.attr('string'),
contacts: DS.hasMany('Contact.Model'),
});
**Approach 2**
//All the different emberjs apps share one namespace but different instances
Yp = Ember.Namespace.extend();
UserRailsEngine = Yp.create();
ContactRailsEngine = Yp.create();
PaymentRailsEngine = Yp.create();
Yp.Jk = Ember.Application.extend();
Yp.Jk.create();
Yp.Router = Em.Router.extend(); **inherits from the Ember.Namespace**
Yp.Store = DS.Store.extend({ }); **inherits from the Ember.Namespace**
console.log(UserRailsEngine.toString()); //UserRailsEngine
console.log(PaymentRailsEngine.toString()); //PaymentRailsEngine
UserRailsEngine.Model = DS.Model.extend
ContactRailsEngine.Model = DS.Model.extend
Can this models share association, they have one namespace but different instance
Contact.Model = ContactRailsEngine.Model .extend({
address: DS.attr('string'),
user: DS.belongsTo('User.Model')
});
User.Model = UserRailsEngine.Modelextend({
name: DS.attr('string'),
contacts: DS.hasMany('Contact.Model')
});
**Approach 3**
//One namespace but multiple subclasses of the namespace for each emberjs app
Mynamespace = Ember.Namespace.extend();
Order = Mynamespace.extend();
OrderRailsEngine = Order.create();
Event = Mynamespace.extend();
EventRailsEngine = Event.create();
console.log(OrderRailsEngine.toString()); //OrderRailsEngine
console.log(EventRailsEngine.toString()); //EventRailsEngine
**Additional questions**
1. Can I still associate ember-data models using hasMany and belongsTo in all of the 3 approach.
I am still not sure how the router will be handled. What do you think the namespace should be in the main-app and each of the rails-engine, so that they still work seamlessly.
What your suggestion on how to handle ember-data DS.Store namespacing since each ember-data model will be namespaced to each engine and I still want the ember-data DS.Store to recognize and work with the the different emberjs models contained in the engines.
Are Ember.Namespace auto-initialized just like Ember.Application is auto-initialized.
Alternative patterns are welcome.
Many thanks for your time.
A modified approah 2 seems to be the emberjs way to go. All the different emberjs apps will still share one namespace but instead of different instances of that namespace, they will share one instance. Final jsfiddle
This approach can be seen from the code pasted below, which was extracted from the link below:
https://github.com/emberjs/data/blob/master/packages/ember-data/tests/integration/embedded/embedded_dirtying_test.js
var attr = DS.attr;
var Post, Comment, User, Vote, Blog;
var Adapter, App;
var adapter, store, post;
var forEach = Ember.EnumerableUtils.forEach;
App = Ember.Namespace.create({ name: "App" });
User = App.User = DS.Model.extend({
name: attr('string')
});
Vote = App.Vote = DS.Model.extend({
voter: attr('string')
});
Comment = App.Comment = DS.Model.extend({
title: attr('string'),
user: DS.belongsTo(User),
votes: DS.hasMany(Vote)
});
Blog = App.Blog = DS.Model.extend({
title: attr('string')
});
Post = App.Post = DS.Model.extend({
title: attr('string'),
comments: DS.hasMany(Comment),
blog: DS.belongsTo(Blog)
});
Adapter = DS.RESTAdapter.extend();
Adapter.map(Comment, {
user: { embedded: 'always' },
votes: { embedded: 'always' }
});
Adapter.map(Post, {
comments: { embedded: 'always' },
blog: { embedded: 'load' }
});
adapter = Adapter.create();
store = DS.Store.create({
adapter: adapter
});
So in the contest of using it across rails engines it becomes:
//App = Ember.Namespace.create();
App = Ember.Namespace.create({ name: "App" });
Main = App.MainApp = Ember.Application.extend();
Main.create();
UserEngine = App.UserEngine = DS.Model.extend({
name: DS.attr('string')
});
VoteEngine = App.VoteEngine = DS.Model.extend({
voter: DS.attr('string')
});
console.log(Main.toString()); //App.MainApp
console.log(UserEngine.toString()); //App.UserEngine
console.log(VoteEngine.toString()); //App.VoteEngine