I'm having to override a route to do some custom loading of models like this:
App.EventsIndexRoute = Ember.Route.extend
model: (params) ->
origin = LJ.origin().join(',')
location = [LJ.stripQuery(params.loc2), params.loc1].join(',')
h = $.param(origin: origin, location: location)
$.getJSON "#{LJ.CONFIG.api.url}/events?#{h}"
The JSON returned includes sideloaded models, but they aren't being loaded by ember. I'm guessing I need to do something to get them loaded but I don't know what. I'd appreciate any help on this.
Here's an example of the returned JSON.
Update
Here's the model definition:
App.Event = DS.Model.extend
acts: DS.hasMany('App.Act')
ageLimit: DS.attr('string')
centsAdvance: DS.attr('number')
centsDoor: DS.attr('number')
currency: DS.attr('string')
description: DS.attr('string')
endsAt: DS.attr('number')
priceAdvance: DS.attr('string')
priceDoor: DS.attr('string')
repeats: DS.attr('string')
repeatsUntil: DS.attr('string')
startsAt: DS.attr('number')
title: DS.attr('string')
url: DS.attr('string')
venue: DS.belongsTo('App.Venue')
venueSection: DS.attr('string')
IMO this has nothing to do with your route but with your model. Make sure that you declare it like that (they have to be present of course)
App.Event = DS.Model.extend({
venue: DS.belongsTo('App.Venue'),
acts: DS.hasMany('App.Act'),
// go on with the model
});
I found it helpful to include a {{ log event }} into the template to dig into the controller and the model and make sure it really does not get loaded
P.s: You do return the ajax response in the route, do you not?
The link of the example JSON you are returning doesn't work, could you please provide a working sample?
You are going around Ember-data for your ajax call, which would mean you will need to handle deserializing it in a more manual manner. If you want ember to do that for you, you need to call into the store, like:
App.EventsRoute = Ember.Route.extend({
model: function() {
// other code here if necessary
return this.store.find('event', params._id);
}
Also, whenever I am not getting my related objects (venues in your case) loaded into the store, it is because the JSON isn't in the format Ember is expecting, which would look like this:
{
Events: [{ Id: 1, Venues: [1], ... }],
Venues: [{ Id: 1, ... }]
}
NOT LIKE THIS:
{
Events: [{ Id: 1, Venue: { Id: 1, ... }, ... }],
}
Maybe this helps?
Related
This is my router:
Router.map(function() {
this.route('merchant', { path:'/merchant/:id' }, function() {
this.route('product-tag');
Currently my api works like this. So I'm trying to get all the product tags that belong to merchant with id: 1781.
http://localhost:3001/merchant/1781/product_tags
The closest I've gotten is using a the product-tag route doing something like this:
model: function() {
debugger;
var parentModel = this.modelFor('merchant').merchant;
return this.store.find('product-tag', { merchant_id: parentModel.id});
}
This will generate a request:
http://localhost:3000/product_tags?merchant_id=1781
I'd assume that because product_tag is a subroute of merchant it'd take into account the dynamic segment of merchant but that doesn't seem to be the case.
Thanks for the help.
My models are as follows:
Merchant:
export default DS.Model.extend({
user_id: DS.attr('number'),
tags: DS.hasMany('product-tag', {async: true})
});
product-tag:
export default DS.Model.extend({
merchant: DS.belongsTo('merchant', {async: true}),
name: DS.attr('string'),
active: DS.attr('boolean'),
taggings_count: DS.attr('number')
});
model hook has two arguments. The first one should content dynamic segments. So, something like this should work:
//Router
Router.map(function() {
this.route('merchant', { path:'/merchant/:merchant_id' }, function() {
this.route('product-tag');
//Route
model: function(params) {
return this.store.find('product-tag', { merchant_id: params.merchant_id});
}
As for the second part of your question, ember data doesn't support nested URLs. Discussion on this subject
Your application routes are unrelated to the API endpoints Ember Data will make requests to. Adapters will build the API request per model.
If you have control of the API server, the easiest way to retrieve merchant's product tags is to send a link with your merchant payload. I don't know what format your API uses, but should be something like:
"merchant": {
"id": "1",
"user_id": "10"
"links": {
"product-tags": "http://localhost:3001/merchant/1781/product_tags"
}
}
I have been looking at EmberJS tutorials, but all of them use FixtureAdapter, and I'm trying to switch to RESTAdapter, but I'm facing a persistent error
Error: Assertion Failed: Expected an object as `data` in a call to push for Books.Book , but was undefined
here's the code for the adapter:
Books.ApplicationAdapter = DS.RESTAdapter.extend({
host: 'http://localhost:24818/api/'});
and calling the api in the router:
Books.BooksRoute = Ember.Route.extend({
model: function() {
return this.store.find('book',1);
}
});
How do I get store.find to return a JSON to be used in the Handlebars template?
Many thanks,
Edit: this is my API response:
[{"Id":1,"Title":"Pride and Prejudice","Year":1813,"Price":9.99,"Genre":"Comedy of manners","AuthorId":1,"Author":null},{"Id":2,"Title":"Northanger Abbey","Year":1817,"Price":12.95,"Genre":"Gothic parody","AuthorId":1,"Author":null},{"Id":3,"Title":"David Copperfield","Year":1850,"Price":15.00,"Genre":"Bildungsroman","AuthorId":2,"Author":null},{"Id":4,"Title":"Don Quixote","Year":1617,"Price":8.95,"Genre":"Picaresque","AuthorId":3,"Author":null}]
Edit: add model definition:
Books.Book = DS.Model.extend({
title: DS.attr('string'),
year: DS.attr('number'),
price: DS.attr('number'),
genre: DS.attr('string'),
authorId: DS.attr('number'),
author: DS.attr('string')
});
I think you need to normalize your response. i.e
"books": [
{
"id":1,
"Title":"Pride and Prejudice",
"Year":1813,
"Price":9.99,
"Genre":"Comedy of manners",
"AuthorId":1,"Author":null
},
{
"id":2,
"Title":"Northanger Abbey",
"Year":1817,
"Price":12.95,
"Genre":"Gothic parody",
"AuthorId":1,
"Author":null
},
{
"id":3,
"Title":"David Copperfield",
"Year":1850,
"Price":15.00,
"Genre":"Bildungsroman",
"AuthorId":2,
"Author":null
},
]
If you have no control over the endpoint. You will need to setup a serializer or normalizer to normalize your response into this expected format.
--
Useful Links
Ember-data Model Maker
Ember JSON Conventions
REST Serializer
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 have two models:
App.Offer = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
products: DS.hasMany('product')
});
App.Product = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
offer: DS.belongsTo('offer')
});
And the server is answering with record and array of ids in this way (for example if the rest adapter asks for /offers/1):
{ "offer": [ { "id": 1, "name": "aaaaaaaaa", "description": "aaaa", "product_ids": [ 1, 2 ] } ] }
but now how can I get the products? I have a route like this:
App.OffersRoute = Ember.Route.extend({
model: function() {
var offer = this.get('store').find('offer', 1);
return offer
}
});
In Ember guide is written that if you want the products you should do:
offer.get('products');
Ok, but where should I put this? in the model hook? in a Controller property?
I've tried many things but I can see no network request to products?id[]=1&id[]=2 as I expected (the server is responding correctly to this request);
Can someone please give an example showing how I can find an offer, its products and use this data in my template?
If you're using the RESTAdapter your data needs to be in this format (if you don't want to return it in this format you can create a custom serializer and fix up the json).
2 differences:
the item under offer shouldn't be an array, since you were looking for a single item it should be an object
the key product_ids should be products, product_ids is the format that the ActiveModelAdapter/ActiveModelSerializer use.
JSON
{
"offer":
{
"id":1,
"name":"aaaaaaaaa",
"description":"aaaa",
"products":[
1,
2
]
}
}
The hasMany relationship should be marked as async if you're expecting it to be returned in a separate payload.
App.Offer = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
products: DS.hasMany('product', {async:true})
});
I hooked it up in jsbin below, but I didn't hook up a result from products?ids[]=1&ids[]=2 (note ids[]=, not id[]=), if you check the network tab you'll see the request being issued (but it'll crash since there is no result).
http://emberjs.jsbin.com/OxIDiVU/345/edit
Has anybody come up with an answer for polymorphic associations and ember-data?
We would need some way of being able to query the type at the other end of the relationship from what I can tell.
Anybody any thoughts on this?
With the latest ember-data build you can now use polymorphic associations:
You need to configure your Models to make it polymorphic:
/* polymorphic hasMany */
App.User = DS.Model.extend({
messages: DS.hasMany(App.Message, {polymorphic: true})
});
App.Message = DS.Model.extend({
created_at: DS.attr('date'),
user: DS.belongsTo(App.User)
});
App.Post = App.Message.extend({
title: DS.attr('string')
});
/* polymorphic belongsTo */
App.Comment = App.Message.extend({
body: DS.attr('string'),
message: DS.belongsTo(App.Message, {polymorphic: true})
});
You also need to configure alias properties on your RESTAdapter
DS.RESTAdapter.configure('App.Post' {
alias: 'post'
});
DS.RESTAdapter.configure('App.Comment' {
alias: 'comment'
});
The result expected from your server should be like this:
{
user: {
id: 3,
// For a polymorphic hasMany
messages: [
{id: 1, type: "post"},
{id: 1, type: "comment"}
]
},
comment: {
id: 1,
// For a polymorphic belongsTo
message_id: 1,
message_type: "post"
}
}
More information in this github thread
So I have something. It's not finished, or entirely clean, but it works. Basically, I use a mixin to bypass the Ember associations entirely. I'm sure that this could be rolled into the adapter or the store, but for now this works.
Polymorphic models come through the the JSON with an itemId and itemType:
App.Follow = DS.Model.extend
user: DS.belongsTo('App.User')
itemId: DS.attr("number")
itemType: DS.attr("string")
I add a mixin to the models that are associated with it :
App.Hashtag = DS.Model.extend App.Polymorphicable,
follows:(->
name: DS.attr("string")
#polymorphicFilter(App.Follow, "Hashtag")
).property('changeCount') #changeCount gives us something to bind to
followers: (->
#get('follows').map((item)->item.get('user'))
).property('follows')
The mixin implements three methods, one that updates the changeCount, one that returns the model's type and the polymorphicFilter method that filters a model by itemType and id:
App.Polymorphicable = Ember.Mixin.create
changeCount: 1
polymorphicFilter: (model, itemType)->
App.store.filter model,
(data) =>
if data.get('itemId')
#get('id') is data.get('itemId').toString() and data.get('itemType') is itemType
itemType:()->
#constructor.toString().split('.')[1]
updatePolymorphicRelationships:()->
#incrementProperty('changeCount')
The controller layer is protected from all this jankyness, except for having to call updatePolymorphicRelationship to make sure the bindings fire:
App.HashtagController = Ember.ObjectController.extend
follow:()->
App.Follow.createRecord({
user: #get('currentUserController.content')
itemId: #get('id')
itemType: #get('content').itemType()
})
#this provides a way to bind and update. Could be refactored into a didSave()
#callback on the polymorphic model.
#get('content').updatePolymorphicRelationships()
App.store.commit()
That's what I have so far. I'm trying to keep things in the model layer as it's just one step removed from the adapter layer. If it looks like Ember Data is not going to look at polymorphics at all in future, then it would make sense to pull this all up to a higher level, but for now, this works and leaves my controllers (relatively) clean.
Polymorphic associations are now supported in ember data
https://github.com/emberjs/data/commit/e4f7c3707217c6ccc0453deee9ecb34bd65c28b9