Multiple models to Ember route - ember.js

I have a list of printers
GradingX.PrintersRoute = Ember.Route.extend({
model: function () {
var printerList = Em.A();
//Ember.
$.getJSON("http://localhost/printers").then(function (data) {
data.forEach(function (item) {
printerList.addObject(item);
}), function () {
alert("$.getJSON failed!");
};
});
return printerList;
},
});
That I'm trying to access from my header
GradingX.HeaderRoute = Ember.Route.extend({
model: function () {
//console.log("test in header model route");
//return Ember.Object.create({
return Ember.RSVP.hash({
printers: What Goes Here?,
otherObjects: More Stuff Here
});
},
});
I'm trying to follow the answer here https://stackoverflow.com/a/20523510/697827, but since I'm not accessing through Ember-Data I don't think this.store.find('printers') is going to get me what I need.
I'm missing something. Please help!

RSVP.hash expects an object with keys and promises as values. So I think that the following could work:
GradingX.HeaderRoute = Ember.Route.extend({
model: function () {
return Ember.RSVP.hash({
printers: $.getJSON("http://localhost/printers"),
otherObjects: $.getJSON("http://localhost/???")
});
},
});
In the referenced answer is used this.store.find, which also returns a promise, but it's resolved with a DS.RecordArray (an array like object provided by ember data). So what matters for RSVP.hash are promises.

Related

Sorting ember-data live array in route model hooks

in a ember route's model hook the following works fine:
model: function () {
return this.modelFor('foo').get('bar');
}
Backed by ember-data, I can delete some of foo's bars somewhere else and it will be updated automagically (live array).
Now, I want this to be sorted (and user in sub-routes, so I have to do this in the route, not in the controller).
model: function () {
return this.modelFor('foo').get('bar')
.then(function (data) {
return data.sortBy('baz');
});
},
... does the job only the first time around, because I'm losing the updating.
Is there a way to write automatic updating sorting in line? What is the ember way to solve this?
To answer my own question, based on the answer from Gaurav:
model: function () {
return this.modelFor('foo').get('bar')
.then(function (data) {
return Ember.ArrayProxy.extend({
arrangedContent: Ember.computed.sort('content', 'props'),
props: ['baz:asc']
}).create({
content: data});
});
},
You can create an Ember Object for your model that has a computed property that is the sorted data.
model: function () {
return this.modelFor('foo').get('bar')
.then(function (data) {
return Ember.Object.extend({
arrangedData: Ember.computed.sort('model', 'props'),
props: ['baz:asc']
}).create({ model: data });
});
},
The Ember.Object.extend part should probably be extracted somewhere so it can be reused in other routes.

Ember: returning just one record using something other than an id

Let's say that I want to have URLs like /users/JoshSmith for maximum readability/shareability.
I set up my Router:
this.resource('user', path: '/users/:username')
And my route:
var UserRoute = Ember.Route.extend({
model: function(params) {
debugger
return this.store.find('user', { username: params.username });
}
});
But this findQuery function actually returns an array, since it's calling /users?username= instead of calling /users/:username like I would normally do.
I'm a little lost as to how I should be handling this; I'm assuming there's a convention out there, I just can't find it.
As suggested here: http://discuss.emberjs.com/t/find-by-different-property/2479
Just override serialize on your route.
var UserRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('user', { username: params.username });
},
serialize: function(model) {
return { username: model.get('username') };
}
});
This replaces the default which looks like this:
serialize: function(model) {
// this will make the URL `/posts/12`
return { post_id: model.id };
}
Source: http://emberjs.com/api/classes/Ember.Route.html#method_serialize
I had this same problem as well, findQuery always returns a record array. The way I got around this was to simply change my model hook in the router to
model: function(params) {
return this.store.find('user', { username: params.username }).then(function(users) {
return users.get('firstObject');
});
}

How to define a nested route to render when hitting the parent in Ember

I have a blog route, and a blog-post route.
Router:
App.Router.map(function () {
this.resource('blog', function () {
this.route('post', {path: ':id/:title'});
});
});
Routes:
App.BlogRoute = Ember.Route.extend({
model: function () {
return this.store.find('BlogPost');
}
});
App.BlogPostRoute = Ember.Route.extend({
model: function (params) {
return this.store.findById('BlogPost', params.id);
},
serialize: function (model, params) {
return {
id: model.get('id'),
title: Ember.String.dasherize(model.get('title'))
}
}
});
In my Handlebars template for the parent blog route I have an {{outlet}} that works fine when I click one of the {{#link-to}}s.
What I want to do is render by default the most recent (highest ID) blog post when a user goes to the /blog route.
I found this question and tried this as a result, to no avail:
App.BlogIndexRoute = Ember.Route.extend({
redirect: function () {
var latest = 3;
this.transitionTo('blog.post', {id: latest});
}
});
(latest is just a placeholder for this.model.pop() or whatever it needs to be.)
I just can't figure out how exactly to load the sub route with the data from the model.
You can fetch the model for any resource/route that has already been fetched (aka parent resources) using modelFor
App.BlogIndexRoute = Ember.Route.extend({
redirect: function () {
var blogs = this.modelFor('blog');
if(blogs.get('length')){
this.transitionTo('blog.post', blogs.get('firstObject')); // or blogs.findBy('id', 123)
}
}
});

Merge two model arrays in a route

I have a controller handling a list of models. These models are of two different types (e.g. Message and Comment). In order to use an ArrayController I would have to merge both lists into one. Is there a way to do this ?
Class-based polymorphism, as proposed in this thread, would solve my problem, but they are not likely to be implemented soon.
In my current solution, I use an ObjectController reveiving both comments and messages. I then merge them using a computed property:
App.SomeRoute = Ember.Route.extend({
model: function (params) {
return Em.Object.create({
comments: this.store.find('comment'),
messages: this.store.find('message'),
});
},
});
App.SomeIndexController = Ember.ObjectController.extend({
merged: Em.computed.union('messages', 'comments'),
});
It works, but I don't benefit from all the niceties of an ArrayController (like sortProperties for example).
What I would like to do is something like:
App.SomeRoute = Ember.Route.extend({
model: function (params) {
var comments = this.store.find('comment');
var messages = this.store.find('message');
return merge(comments, messages);
},
});
where merge returns something similar to what is returned by this.store.find('model').
I asked a similar question recently, here is how I solved the issue.
App.SomeIndexController = Ember.ObjectController.extend({
sortProperties: ['some field'],
sortAscending: false, // false for descending
merged: function() {
var comments = this.get('comment') || [], // This gets wherever you've stored the comments array
messages = this.get('message') || [];// This gets wherever you've stored the messages array
var stream = Ember.A();
stream.pushObjects(comments.toArray());
stream.pushObjects(messages.toArray());
return Em.ArrayProxy.createWithMixins(Ember.SortableMixin, {
content: stream,
sortProperties: this.sortProperties,
sortAscending: this.sortAscending
});
}.property('messages.#each', 'comments.#each')
});
Hope this works for you as well. Just an FYI, for my example, my controller is actually one that is rendered, so I do not set up the model for it in the route. I simply have properties on my controller, lets say, commments and messages that constantly updated themselves as RecordArrays.
So for your example you may need to observe .property('model.messages.#each', 'model.comments.#each')
Inspired by #bmeyers' answer, and after exploring ember-data's source a little bit, I came up with a solution that is reusable and not too terrible. It is probably not optimal, but it does the work.
App.Store = DS.Store.extend({
findMultiple: function (types) {
var self = this;
var recordsByType = types.map(function (type) {
return self.find(type);
});
return self.mergeArrayPromises(recordsByType);
},
mergeArrayPromises: function (promises) {
var promise = Ember.RSVP.all(promises).then(function(arrays) {
var mergedArray = Ember.A();
arrays.forEach(function (records) {
mergedArray.pushObjects(records.toArray());
});
return mergedArray;
});
return DS.PromiseArray.create({
promise: promise,
});
},
});
App.SomeRoute = Ember.Route.extend({
model: function (params) {
return this.store.findMultiple(['comment', 'message']);
},
});
This might help. I stumbled upon this a while back and your question reminded me
https://gist.github.com/sebastianseilund/6096696

Same Ember.JS template for display/edit and creation

I am writing a CRUD application using Ember.JS:
A list of “actions” is displayed;
The user can click on one action to display it, or click on a button to create a new action.
I would like to use the same template for displaying/editing an existing model object and creating a new one.
Here is the router code I use.
App = Ember.Application.create();
App.Router.map(function() {
this.resource('actions', {path: "/actions"}, function() {
this.resource('action', {path: '/:action_id'});
this.route('new', {path: "/new"});
});
});
App.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('actions');
}
});
App.ActionsIndexRoute = Ember.Route.extend({
model: function () {
return App.Action.find();
}
});
App.ActionRoute = Ember.Route.extend({
events: {
submitSave: function () {
this.get("store").commit();
}
}
});
App.ActionsNewRoute = Ember.Route.extend({
renderTemplate: function () {
this.render('action');
},
model: function() {
var action = this.get('store').createRecord(App.Action);
return action;
},
events: {
submitSave: function () {
this.get("store").commit();
}
}
});
The problem is that when I first display an action, and then come back to create a new one, it looks like the template is not using the newly created record, but use instead the one displayed previously.
My interpretation is that the controller and the template are not in sync.
How would you do that?
Maybe there is a simpler way to achieve this?
Here is a JSBin with the code: http://jsbin.com/owiwak/10/edit
By saying this.render('action'), you are not just telling it to use the action template, but also the ActionController, when in fact you want the action template, but with the ActionNewController.
You need to override that:
this.render('action', {
controller: 'actions.new'
});
Updated JS Bin.