How to update Ember.Route model dynamically? - ember.js

I have a route like this:
App.PeopleRoute = Ember.Route.extend({
model: function()
{
return App.Persons.find(personId);
}
});
where personId is loaded asynchronically and is a normal JavaScript variable outside Ember. Now when route is displayed it gets the current PersonId and displays proper data. But when i change the value of personId it does not update the view.
So my question is what is a way to refresh this route to find records with new personId?

This is because model hook is executed only when entered via URL for routes with dynamic segments. Read more about it here.
The easiest solution for this would be to use transitionTo.
App.PeopleRoute = Ember.Route.extend({
model: function(params)
{
return App.Persons.find(params.personId);
},
actions: {
personChanged: function(person){
this.transitionTo("people", person);
}
}
});
App.PeopleController = Em.Controller.extend({
observeID: function(){
this.send("personChanged");
}.observes("model.id");
});

Related

Associating records with Ember Data

I have this setup:
App.MyModel = Em.Model.extend({
someValue: DS.attr('string'),
parent : DS.belongsTo('mymodel',{async:true, inverse:'rooms'}),
rooms : DS.hasMany('mymodel', {async:true, inverse:'parent'})
});
App.MyRoute = Em.Route.extend({
model:function(params){
var parent = this.store.find('mymodel', params.parent_id);
return this.store.createRecord('mymodel',{parent:parent});
}
});
params.parent_id has the ID I want, so find() should return the right record.
Then someValue gets bound in a input box of a template and after input the action create gets called.
App.MyController = Ember.ObjectController.extend({
actions:{
create:function(){
this.get('model').save();
}
}
});
But when the data is send to the server, only someValue has the right data, parent is null.
I don't know if the error is in the model-definition or in the way I set the relationship.
How do I set relations on records correctly?
The promises of the find should resolve to the actual object.
But this didn't work when using it like in the question.
There is a different method, that can be used when the needed record has already been loaded.
model: function(params){
return this.store.createRecord('mymodel', {
'parent': this.store.getById('mymodel', params.parent_id)
});
}
This works, because route I came from already loaded the parent.

EmberJS: Loading a child URL directly in a master-detail app with an SSE-backed model on the master controller

I'm trying to develop this single-module application with a master-detail relationship between two routes. The master is supposed to have a model which is initially loaded and subsequently updated with a single Server-Side-Events entry point. I have the following code so far:
var App = Ember.Application.create({
rootElement: '#content'
});
App.Router.map(function() {
this.resource('receptores', {path: '/'}, function() {
this.resource('receptor', {path: ':user_id'});
});
});
App.ReceptoresController = Ember.Controller.extend({
init: function() {
var sse = new EventSource('/push');
var controller = this;
sse.addEventListener('update-hhs', function(e) {
controller.set('model', JSON.parse(e.data).receptores);
});
}
});
App.ReceptorRoute = Ember.Route.extend({
model: function(params) {
return this.modelFor('receptores').findBy('id', params.user_id);
}
});
Loading the 'receptores' works and currently the data loads fine. Clicking on a link-to generated link as http://localhost:5003/#/skkkd, for example, will load the 'receptor' route and the detail data as well (the detail is just a subset of the same data loaded on the master).
If I try reloading at a detail url or entering http://localhost:5003/#/skkkd directly I'll get the following exception:
Error while loading route: TypeError: Cannot read property 'findBy' of undefined
at App.ReceptorRoute.Ember.Route.extend.model (http://localhost:5003/monitoreo/static/js/receptores.js:34:43)
at superWrapper [as model] (http://localhost:5003/static/js/ember-1.5.1.js:1292:16)
at Ember.Route.Ember.Object.extend.deserialize (http://localhost:5003/static/js/ember-1.5.1.js:36570:19)
at http://localhost:5003/static/js/ember-1.5.1.js:32972:57
at http://localhost:5003/static/js/ember-1.5.1.js:33464:19
at invokeResolver (http://localhost:5003/static/js/ember-1.5.1.js:9646:9)
at new Promise (http://localhost:5003/static/js/ember-1.5.1.js:9632:9)
at Router.async (http://localhost:5003/static/js/ember-1.5.1.js:33463:16)
at Object.HandlerInfo.runSharedModelHook (http://localhost:5003/static/js/ember-1.5.1.js:32971:16)
at Object.UnresolvedHandlerInfoByParam.getModel (http://localhost:5003/static/js/ember-1.5.1.js:33058:19)
I know the problem is the init hook isn't being called for ReceptoresController. I think what I should do is implement a model hook on either a ReceptoresRoute or the controller and load initial data through a jQuery.get() and have the server-side-events only update. But then where do I initialize the SSE?
Edit:
It turns out I misunderstood how the model hook works on child routes. In my case, I normally view the detail page through a link-to link, so Ember already provides the model for receptor. The model hook is never called in that case. It is only called when trying to access the detail page directly which is where it fails. So my problem remains.
My whole problem boils down to where I should place the SSE initialization so that both routes can have access to the same model regardless of which was loaded.
Alright, this is my solution. Pieced together from a couple questions here. Namely https://stackoverflow.com/a/21988143/410224 and https://stackoverflow.com/a/21752808/410224. Hope it helps someone out.
var App = Ember.Application.create({
rootElement: '#content'
});
App.Router.map(function() {
this.resource('receptores', {path: '/'}, function() {
this.resource('receptor', {path: ':user_id'});
});
});
App.ReceptoresRoute = Ember.Route.extend({
model: function() {
var deferredData = Ember.Deferred.create();
var data = [];
var sse = new EventSource('/push');
sse.addEventListener('update-hhs', function(e) {
var receptores = JSON.parse(e.data).receptores;
receptores.forEach(function(r) {
var item = data.findBy('id', r.id);
if (typeof(item) != 'undefined') {
data.replace(data.indexOf(item), 1, [r]);
} else {
data.pushObject(r);
data.sortBy('full_name');
}
});
deferredData.resolve(data);
});
return deferredData;
}
});
App.ReceptorRoute = Ember.Route.extend({
model: function(params) {
return this.modelFor('receptores').findBy('id', params.user_id);
}
});

How to load multiple models sequentially in Ember JS route

What is the best way to load multiple models sequentially in Ember JS?
e.g.
App.ProductRoute = Ember.Route.extend({
model: function(params) {
// first call
this.store.find('Product');
// second call should only be called after the first find is completed
var prod = this.store.find('Product', params.product_id);
// third call should only be called after the above is completed
this.store.find('ProductOptions', prod.get('optionId');
return ???
},
setupController: function(controller, model){
// how would one the set up the controllers?
}
});
This post shows how to load multiple models in a route at the same time.
You can create your own promise, and resolve a hash. That will be the model in the controller, no need to override setupController unless you want them stored on the controller somewhere different.
App.ProductRoute = Ember.Route.extend({
model: function(params) {
var self = this;
return new Em.RSVP.Promise(function(resolve, reject){
// first call
self.store.find('Product').then(function(products){
// second call should only be called after the first find is completed
self.store.find('Product', params.product_id).then(function(product){
// third call should only be called after the above is completed
self.store.find('ProductOptions', product.get('optionId').then(function(productOption){
resolve({
products:products,
product:product,
productOptions:productOptions
});
});
});
});
});
}
});
BTW, it sounds like product options should be an async relationship on the product.

Ember setting a route model to an object from an array of models

Im trying to set a model for a nested route based on the parents array of models.
Msmapp.ClassroomStudentsRoute = Ember.Route.extend({
model: function(params) {
return this.modelFor('classroom').get('classroomStudents');
}
});
Msmapp.ClassroomStudentsStudentRoute = Ember.Route.extend({
model: function(params) {
var studentObjects = this.modelFor('classroom_students'),
studentObject = null;
studentObjects.forEach(function(student) {
if (student.get('id') == params.student_id) {
studentObject = student;
}
});
return studentObject;
}
});
Normally when navigating into this route its fine because im passing along the model, but if I try to copy/paste the url or refresh the page from this point where it has to hit the router i dont get anything.
If I console log studentObject.toString() i can see that its It appears the im returning an Msmapp.Student object so im not sure what im missing or may need to do to get this to work.
These are my routes
this.resource('classrooms', function() {
this.resource('classroom', {path: ':classroom_id'}, function() {
this.route('new_student');
this.resource('classroom_students', function() {
this.route('student', {path: ':student_id'})
});
this.route('new_assignment');
this.resource('classroom_assignments', function() {
this.route('assignment', {path: ':assignment_id'})
});
});
this.route('new');
});
and this initial request for the student objects comes back in the JSON for the classroom.
The classroomStudents is a computed property that loops the student objects and creates an Msmapp.Student object for each one.
Also if I change the route to be Msmapp.ClassroomStudent it works, but its making a request to the server for the object. Then i end up with 2 different instances of the same object, when all i really need is to be able to grab the object from the already setup array of objects.

EmberJS: Load a teaser of a model for a list, full model for detail view

Anyone figure out how to partially load a model for one view, then load the entire model for another?
For example:
/*
This will get all projects, so we only want an id and name returned from the server.
Each project is a monster, so we don't want all data for each project.
*/
App.ProjectsRoute = Ember.Route.extend({
model: function () {
return App.Project.find();
}
});
/*
This is a project detail, so we'd want the entire model returned from the server. Embedded records and all.
*/
App.ProjectRoute = Ember.Route.extend({
});
The closest thing I could find is this: https://stackoverflow.com/a/14183507/1125563
In that I can do something like this:
App.ProjectRoute = Ember.Route.extend({
setupController: function(controller, model) {
if (model.get('isTeaser')){
model.reload();
}
}
});
In this workaround, I have a computed property isTeaser that checks a few things to determine if I've only partially loaded it.
Other than being a little messy, the only deal breaker here is that it's transitioning to the route with a partially loaded model and then after it's loaded, all the stuff will kind of in-elegantly snap in. Not a fan..
Am I missing something obvious?
Here's my approach which eliminates the initial rendering delay. Is that what you meant by 'inelegant snap in'?
// Load the light version of all subjects on page load
App.ApplicationController = Em.Controller.extend({
init: function() {
return App.Subject.find();
}
});
// Fetch all our previously loaded subjects
App.SubjectsRoute = Ember.Route.extend({
model: function() {
return App.Subject.all();
}
});
App.SubjectRoute = Ember.Route.extend({
// If we're loading the page directly to this route, do a normal find
model: function(params) {
return App.Subject.find(params.subject_id);
},
setupController: function(controller, model) {
// Show what details we have for this subject (e.g. the title) immediately
controller.set("model", model);
// Load full details for the model and display them as soon as they arrive
if (Em.isEmpty(model.get("text"))) {
App.Subject.find(model.get("id")).then(function(model) {
return controller.set("model", model);
});
}
}
});