Ember-Data parent template not updating when model is changed - ember.js

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).

Related

this.resource displaying blank page, but returning the model (using console.log() )

i'm currently building an ember app using Yeoman Ember Generator.
this is my template folder structure looks like:
template
|---requisitions
|---draft.hbs
|---pending.hbs
|---waiting.hbs
requisitions.hbs
app.hbs
application.hbs
this is my router.js
Metabuyer.Router.map(function () {
this.route('app');
this.resource('requisitions', function(){
this.resource('draft');
this.resource('pending');
this.resource('waiting');
});
});
in my DS.Store , i have Requisition model which working just fine.
Metabuyer.RequisitionsRoute = Ember.Route.extend({
model: function () {
return this.store.findAll('requisition');
}
});
Draft, pending and waiting route share the same requisition model but filter it based on their needs, like below
Metabuyer.DraftRoute = Ember.Route.extend({
model: function(params){
var filterResult = this.store.filter('requisition', function(requisition){
return requisition.get('state') === 'draft';
});
console.log(test);
return filterResult;
});
}
});
My problem is.
When i use this.resource('draft') in my router nothing is being rendered in my page (blank page), but in my console, the filtered objects are being returned.
if i used this.route('draft') the page is rendered, but the content of the page are not filtered, or should i say, my Metabuyer.DraftRoute is not being called.
Thank you so much for your help, :'(
http://emberjs.com/guides/routing/defining-your-routes/
Routes nested under a resource take the name of the resource plus their name as their route name.
An index route is also needed on parent routes.
So navigating to /requistions loads up the RequesitionsRoute and RequisitionsIndexRoute you need to set the model on RequisitionsIndexRoute and use RequisitionsIndexControlleretc. You will need to rename requisitions.hbs to index.hbs and move it to the requisitions directory.
You also need to prefix your draft route object name with the parent so DraftRoute becomes RequisitionsDraftRoute and the same for controllers, views etc.

Nested routes — creating records that are async

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

Rendering a template and invoking model hook in Ember.js

I am using JSON requests to populate the model for a template called example.
App.ExampleRoute = Ember.Route.extend({
model: function() {
var url = "example/GetExamples";
return Ember.$.getJSON(url).then(function(data) {
return data;
});
}
});
The example template perfectly renders this data. Now my backend might change from time to time as I have another template called addExample. It just sends a JSON request to the server to add an entry in the database from this other template. After adding the examples, I try to transition to example template but I see old data only. On refreshing the page, the new data with the added entry comes up.
App.AddController=Ember.Controller.extend({
postAddRequest: function() {
var request = $.post("/example/AddExample", {params});
request.then(this.success.bind(this), this.failure.bind(this));
},
success: function(data) {
this.transitionToRoute('example');
}
});
So is there any way I can force Ember to re render 'example' template and invoke its model hook in the route without refreshing the page. I can't use ember-data for persistence as it is still in beta stage.
Transition to the route and send the data you'd like to use on the route. Additionally the route should be using an id/slug in the url. If not see below
this.transitionToRoute('example', data);
If you are already on the route and you want to update the model of a currently set controller you can grab that controller and set the model.
From the controller
App.SomeController = Em.Controller.extend({
needs: ['example']
someFunction: function(){
var model = ... get new model;
this.get('controllers.example').set('model', model);
}
});

Computed property for the number of records in the store?

This may be abusing Ember, but I want to create a computed property for the number of items in the store.
I'm trying to prototype a UI that exists entirely client-side. I'm using fixture data with the local storage adapter. That way, I can start off with canned data, but I can also add data and have it persist across reloads.
As I'm currently working on the data layer, I've built a settings route that gives me a UI to reset various models. I would like to add a Handlebars expression like {{modelCount}} so I can see how many records there are in the store. That's quicker than using the Ember Data Chrome extension, which resets to the routes tab on every page reload.
The following will show me the number of records once, but does not change when the number of records changes:
modelCount: function() {
var self = this;
this.store.find("my_model").then(function(records) {
self.set("modelCount", records.get("length"));
});
}.property()
I get that the store is supposed to proxy an API in the real world, and find returns a promise, but that's about the limit of my knowledge. I don't know how tell Ember to that I want to know how many records there are, or if this is even a valid question.
Load the result of store.find into an Ember.ArrayController's content and then bind the length of content to modelCount. An example:
App.SomeRoute = Ember.Route.extend({
model: function(){
return this.store.find('my_model');
}
});
App.SomeController = Ember.ArrayController.extend({
modelCount: Ember.computed.alias('content.length')
});
See a working example in http://jsbin.com/iCuzIJE/1/edit.
I found a workable solution by combining the answer from #panagiotis, and a similar question, How to load multiple models sequentially in Ember JS route.
In my router, I sequentially load my models:
model: function() {
var self = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
self.store.find("model1").then(function(model1) {
self.store.find("model2").then(function(model2) {
self.store.find("model3").then(function(model3) {
resolve({
model1: model1,
model2: model2,
model3: model3
});
});
});
});
});
},
Then in my controller, I define simple computed properties:
model1Count: function() {
return this.get("model1.length");
}.property("model1.length"),
...

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