confused about asyc loading from direct url emberjs - ember.js

Below are my fixture adapters.
My understanding is model hook is not called on transitionTo or link-to when a model is provided.
But when same route is reached direactly by sharing or copy pasting url model hook is called.
Now when I transition by passing a model I see the city 1 and city 2 properly.
But when I copy past url the cities are not displayed. I tried .then and then get cities still I cannot see it. I have commented that line. I know I am doing something stupid. I did googled but couldnt figure out.
here is my jsbin: BIN BIN BIN
Though this is similar to THiS question. the ans is use modelFor and then findBy. but modelFor gives model for the parent route. but in my case since its not nested routes. this.modelFor('countries') gives undefines and hence i cannot apply findBy on it.
model: function(params) {
this.store.find('country', params.countryCode).then(function(country) {
console.log(country);
//country.get('cities');
});
}
Q.Country.FIXTURES = [{
id: 1,
countryCode: "CO",
countryName: "Country",
cities: [1, 2]
}];
Q.City.FIXTURES = [{
id: 1,
cityName: "city 1",
country: 1
}, {
id: 2,
cityName: "city 2",
country: 1
}];
Q.CountryRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('country', params.countryCode);
},
serialize: function(country) {
return {
country_id: country.get("countryCode")
};
},
afterModel: function(model) {
console.log("after model was called");
//this.transitionTo('cities',model);
}
});
Q.Router.map(function() {
this.resource("countries");
this.resource('country', {
path: ':country_id'
});
});

You have to return something from your model() hook. No need to use then() because in the model hook Ember will automatically wait for the promise to resolve.
model: function(params) {
return this.store.find('country', params.country_id);
}
If you want to use a slug, something like this could work:
Q.Router.map(function() {
this.resource("countries");
this.resource('country', {
path: ':country_code'
});
});
model: function(params) {
return this.store.findQuery('country', { code: params.country_code });
}

Related

Ember-data return a single json object with store.find

Is this possible? I know I can do:
this.store.find('model', 1)
but that's not what I want. I would like to retrieve the json in this format: (working if I retrieve it in the route like this ):
App.OptionsRoute = Ember.Route.extend({
model: function () {
return {
"options":{
"id": "1",
"headline": "A Headline",
"results": [
{
"id": "1",
"title": 'Option 1',
},
{
"id": "2",
"title": "Option 2"
}
]
}
};
}
});
options model:
App.Options = DS.Model.extend({
headline: DS.attr(),
results: DS.attr()
});
options.hbs
<h5>{{options.headline}}</h5>
{{#each item in options.results}}
<h5>{{item.title}}</h5>
{{/each}}
I am using the RESTAdapter. And that is the only model that will be retrieved on that route. I would like to be able to use ember-data, but store.find expects an array.
You're missing a point here. First of all you're using bad format for your response. You need custom serializer. You can also use a bit more dirty workaround like this(but it works). Route:
App.OptionsRoute = Ember.Route.extend({
model: function() {
that = this;
return new Promise(function (resolve, reject) {
url = that.store.adapterFor('option').buildURL('option');
Ember.$.getJSON(url).then(function (json) {
body = json.options;
correct = {
options: [
body
]
};
that.store.pushPayload('option', correct);
resolve(that.store.all('option').get('firstObject'));
});
});
}
});
Template:
<h5>{{model.headline}}</h5>
{{#each item in model.results}}
<h5>{{item.title}}</h5>
{{/each}}
Application outputs:
A Headline
Option 1
Option 2
Working demo - please notice that I'm using $.mockjax to recreate your response from server, but it matches format you provided.

How to tell ember to uniquely identify records by another property?

How can I let ember know that I want it to uniquely find records by the slug instead of id .
In my route model I can do something like this
model: function(params) {
return this.store.find('anime', params.anime_slug);
},
serialize: function(anime) {
return { anime_slug: anime.get('anime_slug') };
},
but whenever I call something like model.reload() it tries to reload with the record's id and not the record's slug. So how do I let ember know to uniquely identify the record use anime_slug and not id.
You're almost there!
Route:
App.AnimeRoute = Em.Route.extend({
model: function(params) {
return this.store.find('anime', { anime_slug: params.anime_slug }).then(function(results) {
return results.objectAt(0);
});
},
serialize: function(model) {
return { anime_slug: model.get('anime_slug') };
},
});
Note the change to the model method.
Router:
App.Router.map(function() {
this.resource('anime', { path: '/anime/:anime_slug' });
});

Implementing model filter

I have set up the following scaffolding for my Ember application.
window.App = Ember.Application.create({});
App.Router.map(function () {
this.resource('coaches', function() {
this.resource('coach', {path: "/:person_id"});
});
});
App.ApplicationAdapter = DS.FixtureAdapter.extend({});
App.Person = DS.Model.extend({
fname: DS.attr('string')
,lname: DS.attr('string')
,sport: DS.attr('string')
,bio: DS.attr('string')
,coach: DS.attr('boolean')
,athlete: DS.attr('boolean')
});
App.Person.FIXTURES = [
{
id: 10
,fname: 'Jonny'
,lname: 'Batman'
,sport: 'Couch Luge'
,bio: 'Blah, blah, blah'
,coach: true
,athlete: true
}
,{
id: 11
,fname: 'Jimmy'
,lname: 'Falcon'
,sport: 'Cycling'
,bio: 'Yada, yada, yada'
,coach: false
,athlete: true
}
];
I am trying to set up a route to filter the person model and return only coaches. Just to make sure I can access the data, I have simply used a findAll on the person model.
App.CoachesRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('person');
}
});
Now though, I am trying to implement the filter method detailed on the bottom of the Ember.js Models - FAQ page.
App.CoachesRoute = Ember.Route.extend({
model: function() {
var store = this.store;
return store.filter('coaches', { coach: true }, function(coaches) {
return coaches.get('isCoach');
});
}
});
The coaches route is not working at all with the new route implemented and the old one commented out. I am using the Ember Chrome extension and when using the filter route the console responds with, Error while loading route: Error: No model was found for 'coaches'. Apparently the route is not working, specifically the model. No kidding, right? What am I missing in my filter model route?
Thank you in advance for your help.
The error message is spot on- there is no CoachModel. I think you need to do this:
App.CoachesRoute = Ember.Route.extend({
model: function() {
var store = this.store;
return store.filter('person', { coach: true }, function(coaches) {
return coaches.get('isCoach');
});
}
});

How to correctly access a parent controllers data (model) in nested resources?

I have a little EmberJS app to test things out hot to do nested resources. Sometimes accessing a parent routes/controllers data work, other times not.
Most likely this is due to a oversight on my part with how EmberJS does its magic.
Here is the app:
window.App = Ember.Application.create();
App.Router.map(function() {
this.resource('items', function() {
this.resource('item', {path: ':item_id'}, function() {
this.resource('subitems');
});
});
});
App.ApplicationController = Ember.Controller.extend({
model: {
items: [
{
id: 1,
name: 'One',
subitems: [
{
id: 1,
name: 'One One'
}, {
id: 2,
name: 'One Two'
}
]
}, {
id: 2,
name: 'Two',
subitems: [
{
id: 3,
name: 'Two One'
}, {
id: 4,
name: 'Two Two'
}
]
}
]
}
});
App.ItemsRoute = Ember.Route.extend({
model: function() {
return this.controllerFor('Application').get('model.items')
}
});
App.ItemRoute = Ember.Route.extend({
model: function(params) {
var items = this.controllerFor('Items').get('model')
var item = items.filterBy('id', parseInt(params.item_id))[0]
return item
}
});
App.SubitemsRoute = Ember.Route.extend({
model: function(params) {
var item = this.controllerFor('Item').get('model')
var subitems = item.get('subitems')
return subitems
}
});
http://jsfiddle.net/maxigs/cCawE/
Here are my questions:
Navigating to items/1/subitems throws an error:
Error while loading route: TypeError {} ember.js:382
Uncaught TypeError: Cannot call method 'get' of undefined test:67
Which i don't really get, since apparently the ItemController loads its data correctly (it shows up) and the same construct works for the ItemsRoute as well to get its data.
Since i don't have access to the parents routes params (item_id) i have no other way of re-fetching the data, even though directly accessing the data from ApplicationController works fine.
Why do i have define the root data in a controller not route?
Moving the model definition from ApplicationController to ApplicationRoute, does not work.
Conceptually, as far as i understand it, however this should even be the correct way to do it, since everywhere else i define the mode data for the controller int he route.
Or should the whole thing be better done via the controllers needs-api? As far as i understood the needs are more for only accessing extra data within the controller (or its view) but the routers job is to provide the model.
1. Navigating to items/1/subitems throws an error:
Your model is just a javascript object so there isn't a method get to fetch the data. You can access the subitems by just executing item.subitems.
Also the argument of controllerFor() should be lower case.
For instance:
this.controllerFor('application')
2. Why do i have define the root data in a controller not route?
You can set the model from the route in the setupController method.
App.ApplicationRoute = Ember.Route.extend({
setupController: function(controller) {
controller.set('model', { ... });
}
});
http://jsfiddle.net/Y9kZP/
After some more fiddling around here is a working version of the example in the question:
window.App = Ember.Application.create();
App.Router.map(function() {
this.resource('items', function() {
this.resource('item', {path: ':item_id'}, function() {
this.resource('subitems');
});
});
});
App.ApplicationRoute = Ember.Route.extend({
model: function() {
return Ember.Object.create({
items: [
Ember.Object.create({
id: 1,
name: 'One',
subitems: [
{
id: 1,
name: 'One One'
}, {
id: 2,
name: 'One Two'
}
]
}), Ember.Object.create({
id: 2,
name: 'Two',
subitems: [
{
id: 3,
name: 'Two One'
}, {
id: 4,
name: 'Two Two'
}
]
})
]
})
}
});
App.ItemsRoute = Ember.Route.extend({
model: function() {
return this.modelFor('application').get('items')
}
});
App.ItemRoute = Ember.Route.extend({
model: function(params) {
return this.modelFor('items').findProperty('id', parseInt(params.item_id))
}
});
App.SubitemsRoute = Ember.Route.extend({
model: function(params) {
return this.modelFor('item').get('subitems')
}
});
http://jsfiddle.net/maxigs/cCawE/6/ and deep link into subitems (that did not work previously) http://fiddle.jshell.net/maxigs/cCawE/6/show/#/items/2/subitems
What changed:
root-model data moved into ApplicationRoute
root-model moved into an ember object, and sub-objects are also their own ember objects (so calling get('subitems') and other ember magic works)
changed all the controllerFor('xxx').get('model') into modelFor('xxx') (lower case!), which probably has no effect other than consistency
I'm still not sure if this is now "the ember way" of doing what i have here but its consistent and works completely as wanted.

EmberJS: Use AJAX data from parent resource in child route

Let's say that I want to create an ember app that displays a matrix and allows
you to click on a cell for more information. My routes look like this:
App.Router.map(function() {
this.resource("data", function() {
this.route("cell", {path: ':row/:col'});
});
});
App.DataRoute = Ember.Route.extend({
model: function(params) {
return App.DataModel.create({
/* information needed for AJAX query */
});
},
});
App.DataCellRoute = Ember.Route.extend({
model: function(params) {
return Ember.Object.create({
row: params.row,
col: params.col
});
},
serialize: function(model) {
return {row: model.row, col: model.col};
},
});
Furthermore, the matrix load is expensive (say, a slow AJAX request), so I
don't want to have to reload the data when I transition to the child route.
Also, assume that the data in the cells can't be serialized, so I can't just
pass the cell data in the URL.
Here's what the controllers looks like:
App.DataController = Ember.ArrayController.extend({
model: null,
content: Ember.A(),
loadData: function() {
self = this;
$.ajax({
/* ... */,
success: function(data) {
App.beginPropertyChanges();
self.clear();
data.forEach(function(row) {
self.pushObject(row);
});
App.endPropertyChanges();
},
}.observes('model'),
});
App.DataCellController = Ember.ObjectController.extend({
needs: 'data',
model: {row: 0, col: 0},
matrixBinding: 'controllers.data.content',
cell: function() {
var xy = this.get('model');
return this.get('matrix')[xy.row][xy.col];
}.property('matrix.#each', 'model'),
});
When you click on a cell, its view tells DataController to send an event that
transitons to data.cell with the appropriate row/column. I expected that
when I transition to the data.cell route, I should have access to
DataController's content. However, all I get is the default empty array.
What am I doing wrong?
EDIT:
To answer the question 'how is DataController.content set', I updated the
question to show a more accurate depicition of DataRoute and
DataController. Basically, DataController has a model that contains
information pertinent to the AJAX request. I have a function, loadData,
which observes the model and loads content.