How to display findAll from JSON with Ember-data, in View? - ember.js

I'd like to display a list populated with data from a JSON file. I would use a modified Adapter. Here's the code so far, including path to the test json file (since I'd hope to get some help without the use of FIXTURES)
I'm unsure on how to pushObject for results using Ember-Data. I know I'm probably still not getting some concepts for Ember-Data.
My question is: How do I get a list from the JSON file based on the following code.
JavaScript
App = Em.Application.create();
App.store = DS.Store.create({
revision: 4,
adapter: App.adapter
});
App.adapter = DS.Adapter.create({
findAll: function (store, type) {
console.log('finding');
var url = type.url;
$.getJSON(url, function(data) {
store.loadMany(type, data);
});
}
});
App.Person = DS.Model.extend({
fName: DS.attr('string'),
surname: DS.attr('string'),
url: 'test.json',
});
App.personController = Ember.ArrayController.create({
content: [],
init: function(){
this._super();// create an instance of the person model
this.set('person', App.store.findAll(App.Person));
},
});
HTML
<script type="text/x-handlebars">
{{#each App.personController}}
<p>{{fName}} {{surname}}</p>
{{/each}}
</script>
EDIT
I´ve updated the code in the jsfiddle on #pauldechov recommendations, still no luck.
Here's the content for test.json, so the whole app can be recreated:
[{"name": "Bruce","surname": "Banner"},
{"name": "Peter","surname": "Parker"},
{"name": "Natasha","surname": "Romanoff"},
{"name": "Steve","surname": "Rogers"},
{"name": "Tony","surname": "Stark"}]

The url property should be defined as such:
App.Person = DS.Model.extend({
fName: DS.attr('string'),
surname: DS.attr('string'),
}).reopenClass({ url: 'test.json' });
...as an instance variable rather than as a class variable (see example: https://github.com/emberjs/data#find). The way you defined it (or if you use .reopen({...}) instead of .reopenClass({...}), it would be accessible via (a specific) person.url rather than type.url.
AFAIK you can't add a .json file to jsfiddle's resources; it won't be accessible. Querying the full url of test.json yielded a cross domain issue.
You may need to place the model(s) above App.adapter, above App.store. Try this if it's still failing.
Peripheral observation: "name" in test.json vs. "fName" in the code?

App.personController = Ember.ArrayController.create({
content: [],
init: function(){
this._super();// create an instance of the person model
this.set('person', App.store.findAll(App.Person));
},
});
Why set person? Should it not be set content?

Related

Emberdata How to make a request on a dynamic segment

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"
}
}

switch to RESTAdapter - EmberJS

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

How to load hasMany in Ember?

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

ember.js sideloaded models not working

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?

How to use Ember-Data associations in conjunction with an ArrayProxy

I am trying to migrated my app to using Ember-Data as it's persistence mechanism. One thing that strikes me is that I'm not sure if it's still possible to use an arrayProxy for aggregate properties of a hasMany association. In my previous iteration I didn't have any explicit associations, just controllers tied together by specific properties. Now I'd like to take advantage of the association functionality in ember-data, but I am getting errors when I trie to bind the content of my array proxy to the "children" property of the DS.Model. My code is below and there is a jsfiddle here: http://jsfiddle.net/sohara/7p6gb/22/
The error I get is:
Uncaught TypeError: Object App.recipeController.content.ingredients has no method 'addArrayObserver'
I would like to be able to retain a controller layer, even if the data associations are controlleed at the model level. It'd also (ideally) like the child objects to be embedded in the json representation of the parent object in order to avoid multiple server requests.
window.App = Ember.Application.create();
App.store = DS.Store.create({
revision: 3,
adapter: DS.fixtureAdapter
});
App.Ingredient = DS.Model.extend({
name: DS.attr('string'),
price: DS.attr('string')
});
App.Recipe = DS.Model.extend({
name: DS.attr('string'),
ingredients: DS.hasMany('App.Ingredient', {embedded: true} )
});
App.Recipe.FIXTURES = [
{id: 1, name: 'Pizza', ingredients: [{id: 1, name: 'tomato sauce', price: 2, recipeId: 1}]}
];
App.recipeController = Ember.Object.create({
content: App.store.find(App.Recipe, 1)
});
App.ingredientsController = Ember.ArrayProxy.create({
content: 'App.recipeController.content.ingredients',
totalWeigth: function() {
var price = 0;
items = this.get('content');
items.forEach(function(item) {
weight += price;
});
}.property()
});
In App.ingredientsController you need to have contentBinding: 'App.recipeController.content.ingredients', instead of content: ...