Nested routes — creating records that are async - ember.js

I have a simple Ember app with 2 models related to each other: parent and record:
// parent model
export default DS.Model.extend({
records: DS.hasMany('records', { async: true })
});
// record model
export default DS.Model.extend({
parent: DS.belongsTo('parent')
});
Record routes are nested under parent routes and parent template contains link-to records and an outlet to render the content in. Records template contains simple {{#each}} loop.
Here is the records index route that is responsible for setting model:
// records index route
export default Ember.Route.extend({
model: function() {
return this.modelFor('parent').get('records');
}
});
When I load parent/records route all records load/render fine. The problem starts when I try to create new record; which I do in records index controller:
// records index controller
export default Ember.ArrayController.extend({
needs: ['parent'],
actions: {
createRecord: function() {
var newRecord = this.store.createRecord('record', {
parent: this.get('controllers.parent.model')
});
newRecord.save();
}
}
});
After I create new record it doesn't appear inside my records index template. If I open Ember inspector I can see the record there, it even has the correct parent record assigned.
Here is the answer I am getting from the server after creating the record:
{ "record": { "id": 1, "parent_id": 1 }}
I think the problem occurs because of record index route or response from the server; if I change route like this, new records appear as intended after creation (although thats not what I want; I want just the records that belong to the parent record):
// records index route
export default Ember.Route.extend({
model: function() {
return this.store.find('record');
}
});
I am on Ember 1.6.1 and Ember Data beta 8. Any ideas what might be wrong?

You're correct, your index route grabs an Array of models — it doesn't know that they have been updated or removed.
There are two ways to solve this: use a filter array or update the array manually.
Here is a link to question on the same subject:
Ember template not updating from arraycontroller

Related

filling data to the ember controller without router

I try fill ember controller without setupController hook, like this:
Ember.$.getJSON('http://url.com').then(function (response) {
self.store.pushPayload('record', response);
self.get('controllers.consultation').set('model', self.store.push('consultation', response.consultation));
});
Model for controller consultation contain hasMany field records
export default DS.Model.extend({
records: DS.hasMany('record', { async: true }),
....
});
When I call controller.records in templates it's doesn't works (empty), but other fields works fine.
Question: why controller.records empty?
NOTE: When I create field in consultation controller which contain consultation object it's works.
self.get('controllers.consultation').set('controllerObj', self.get('controllers.consultation').set('model', self.store.push('consultation', response.consultation)));
I'd try to resolve model.records immediately after model.
var controller = this.get('controllers.consultation');
Ember.$.getJSON('http://url.com').then(function (response) {
// ... your magic with store
// var consultation = consultation instance extracted from response
controller.set('model', consultation);
return consultation.get('records'); // or your custom api request for model.records
}).then(function (records) {
controller.set('modelRecords', records) // or your custom request records extraction
});
Then in controller you could set computed records:
// controller
records: function() {
// your logic here
}.property('modelRecords.[]')
and {{records}} should work in template
//template
{{records.length}}

How to get page to update with new records from server without a route change?

I have an Ember app that uses server-side storage. In the products route, I have a list of products that I display in my product route (which are fetched in the usual way upon entering the route)
{{#each item in sortedProducts}}
{{/each}}
....
fetch
App.ProductRoute = Ember.Route.extend({
model: function(){
return Ember.RSVP.hash({
store: this.store.find('product'),
//other code ommitted
})
}
})
In the controller, I do the sorting
sortProperties: ['date:desc'] //dated added
sortedProducts: Ember.computed.sort('model', 'sortProperties'),
This works, however, I give the user the option to filter the records displayed. Upon clicking a button, an action is called that queries the server for a subset of records (it doesn't just filter the records that are already in the store cache)
actions: {
filterByPriceAndColor: function(){
this.store.find('product', {price: pricevariable, color: colorvariable});
}
}
This queries and returns the desired records, but the list on the page isn't updated i.e. the list on the page still displays all the records that are fetched upon application load.
Question: how do I get the page to update with the new records fetched from the server without a route change, (and will the solution integrate with the computed sort that already exists for ordering the entries by date)
To update your model from an action (or anywhere else) you simple need to set a new value for it and Ember will to the hard work for you.
In your case it should look like this:
actions: {
filterByPriceAndColor: function() {
var promise = this.store.find('product', {price: pricevariable, color: colorvariable});
var self = this;
promise.then(function(data) {
self.set('model', data);
});
}
}
Here is a JSBin demonstrating how it works: http://emberjs.jsbin.com/walunokaro/3/edit

How do I reload data and update a bound element after a user clicks a button?

Why is it that when I click 'Random', the information in the template isn't reset and the data isn't update?
I have data that I want to display after a REST endpoint is successfully reached. The REST data that's returned is a random database record, so I don't need to worry about randomizing my request or anything. I only need to reach the server via that URL. In this case, the URL is: localhost:8000/api/verses/0
My handlebars template looks like this:
app/templates/verses.hbs
<div id="panel">
<h3>{{model.reference_number}}
<h3>{{model.body}}</h3>
<button {{action "getAnotherVerse"}}>Random</button>
</div>
{{outlet}}
So, when the 'Random' button is clicked, the following should be invoked:
app/controllers/verses.js
import Ember from 'ember';
import DS from 'ember-data';
export default Ember.ObjectController.extend({
actions: {
getAnotherVerse: function() {
this.get('model').reload();
// This is where the text should be reset to the new data.
}
}
});
app/routers/verses.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.find('verse', '0');
}
});
When you fire getAnotherVerse you just take the current record(model) and simply reload it to fetch its latest data. I guess you want to call model method of your route once again, so model will be reset and you'll get brand new record from your server.
Move getAnotherVerse to your VersesRoute where you specify model for VersesController and try following code:
# app/routes/verses.js
model: function() {
return this.store.find('verse', '0');
},
actions: {
getAnotherVerse: function() {
this.refresh(); # beforeModel, model, afterModel, setupController will re-fire
}
}
If this still doesn't work, please try this:
# app/routes/verses.js
model: function() {
return this.store.fetch('verse', '0');
},
actions: {
getAnotherVerse: function() {
this.store.unloadAll('verse'); # I assume `verse` is your Model name
this.refresh(); # beforeModel, model, afterModel, setupController will re-fire
}
}
Your telling Ember Data to find the record with id = 0. Just guessing that your API endpoint is treating 0 as a special case and returning a record that does have an actual id.
Because Ember Data is using an identity map under the hood I'm guessing that when you call reload the data is creating a new record in the store. And therefore isn't triggering updates on the record that is being used for the model.
A better approach would be to just use
var that = this;
Ember.$.get('localhost:8000/api/verses/0')
.then(function(data) {
that.set('model', data);
});
You could push the data into the store too http://emberjs.com/guides/models/pushing-records-into-the-store/ and then it would be available if you need to find it by id later.
Another approach would be to create a custom adapter / serializer that could hide some of this, really depends on how your using ember data outside of this use case.

Ember-Data parent template not updating when model is changed

I am using EAK with a simple nested routing structure, but changing the parent model in the child controller does not change the top-level view. For example if I have the following router.js file:
this.resource('similar', function() {
this.resource('list', { path: '/list/:phone_id' });
this.resource('upload');
this.resource('new');
});
For the 'similar' route model I am using ember-data together with the RESTAdapter which is backed Flask.
export default Ember.Route.extend({
model: function() {
return this.store.find('phone');
}
});
If I manipulate the model inside the 'upload' controller then the changes are not reflected in the template, e.g.
var record = this.store.createRecord('phone', {
numbers: [1,2,3,4]
});
record.save();
will not change "{{#each list in model}} {{list.numbers}} {{/each}}".
If I reload the page it works fine. What am I doing wrong?
Instead of store.find which hits the server and stores that exact list, try using store.filter. store.filter "remains up to date as new records are loaded into the store or created locally"
http://emberjs.com/api/data/classes/DS.Store.html#method_filter
The problem here was that the REST endpoint did not return the new record (with the id attribute set).

How to use "needs" with nested routes / controllers in emberjs RC 2

I have a very basic route setup that allows me to first show "all" records for some object. Then if the user selects a dropdown they can filter this down using a date.
I recently upgraded to RC2 and realized that "needs" has replaced or will soon replace controllerFor.
I'm curious how I can use "needs" in the below situation where I need the nested / inner route for "records.date" to change the content for the parent "records" route when a date is selected.
What is missing from below is that inside the App.RecordsDateRoute I need to change the content of the "records" controller to be a new filter (by date this time) and everything I seem to do just dumps the handlebars template and show nothing -even when I try to use something simple like
this.controllerFor("records").set('content', App.Record.find(new Date(model.loaded)))
from within the setupController method of the RecordsDateRoute
App.Router.map(function(match) {
return this.resource("records", { path: "/" }, function() {
return this.route("date", { path: "/:date_loaded" });
});
});
App.RecordsController = Ember.ArrayController.extend({
selected: 0,
dates: Ember.computed(function() {
return App.Date.find();
}).property()
});
App.RecordsIndexRoute = Ember.Route.extend({
model: function() {
this.controllerFor("records").set("selected", 0);
return App.Record.find();
}
});
App.RecordsDateRoute = Ember.Route.extend({
model: function(params) {
//the controllerFor below seems to be working great ... but what about needs?
this.controllerFor("records").set("selected", params.date_loaded);
return App.Date.create({ loaded: params.date_loaded });
}
});
With rc2, instances of other controllers can be retrieved via "controllers.controllerName", in you case it would be this.get('controllers.records').
The "needs" declaration makes the referencing controller sort of import the reference to the other controller; in your case, the date controller would be:
App.RecordsDateRoute = Ember.Route.extend({
needs: ['records'],
model: function(params) {
this.get("controllers.records").set("selected", params.date_loaded);
return App.Date.create({ loaded: params.date_loaded });
}
});
Regarding App.Record.find(new Date(model.loaded)), find() expects an id or an object whose keys and values will be used to filter the collection of models, but you're giving it a Javascript date.
Did you mean App.Record.find(new App.Date(model.loaded)), or maybe something like App.Record.find({ loaded: model.loaded }) /* assuming it's already a Date */?
There is also an initController(controller, model) method in the route called , maybe you could use that instead of "overloading" the model() method with too many responsibilities. http://emberjs.com/api/classes/Ember.Route.html#method_setupController
I recently upgraded to RC2 and realized that "needs" has replaced or will soon replace controllerFor.
To access another controller from route hooks you should continue to use controllerFor. Controller.needs is for communication between controllers, it replaces the now deprecated use of controllerFor method on controllers. AFAIK there is no plan to deprecate controllerFor on ember Routes.
I'm curious how I can use "needs" in the below situation where I need the nested / inner route for "records.date" to change the content for the parent "records" route when a date is selected.
For this use case it would be best to stick with controllerFor. It is possible to use needs this way, by specifying that App.RecordsDateController needs = ['records'] you could access the records controller via controller.get('controllers.records') from within your route's setupController hook.
What is missing from below is that inside the App.RecordsDateRoute I need to change the content of the "records" controller to be a new filter (by date this time) and everything I seem to do just dumps the handlebars template and show nothing -even when I try to use something simple like this.controllerFor("records").set('content', App.Record.find(new Date(model.loaded))) from within the setupController method of the RecordsDateRoute
App.RecordsDateRoute = Ember.Route.extend({
model: function(params) {
return App.Date.create({ loaded: params.date_loaded });
},
setupController: function(controller, model) {
var recordsController = this.controllerFor("records");
// Moved this from model hook, since here you are 'setting up a controller'
recordsController.set("selected", model.date_loaded);
// Set query based on current route's model
var query = { loaded: model.loaded };
recordsController.set("content", App.Record.find(query));
}
});