Redirecting to first item in ArrayController - ember.js

I am trying to redirect to the first item in an ArrayController. I have found a few other questions related to this, but none had answers that seemed to work (a lot of changes have been happening so this is understandable).
One particular answer from Yehuda here:
App.DoctorsRoute = Ember.Route.extend({
model: function() {
return App.Doctor.find();
},
redirect: function() {
var doctor = this.modelFor('doctors').get('firstObject');
this.transitionTo('doctor', doctor);
}
});
I 'think' I have recreated this scenario, but I must have done something wrong...
Any insight into what I am doing wrong is greatly appreciated.
Example JSBin here.

The problem here is that your list of models is not yet loaded from the server. Depending on your needs i would recomend using a promis to let the rout wait abit until your model is loaded.
App.DoctorsRoute = Ember.Route.extend({
model: function() {
return App.Doctor.find().then(function (list) {
return list.get('firstObject');
});
},
redirect: function() {
var doctor = this.modelFor('doctors');
this.transitionTo('doctor', doctor);
}
});
ofcourse.. wel that would make the redirect abit silly, so if you just want to wait for the list to load you could try:
App.DoctorsRoute = Ember.Route.extend({
model: function() {
return App.Doctor.find();
},
redirect: function() {
var self = this;
this.modelFor('doctors').then(function (list) {
return list.get('firstObject');
}).then(function (doctor){
if(!doctor)
self.transitionTo('doctor', doctor);
});
}
});

Related

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

Multiple models to Ember route

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.

How to avoid too many empty records?

Ember : 1.5.0-beta.2
Ember Data : 1.0.0-beta.7
I have the following router:
App.Router.map(function() {
this.resource('posts', function() {
this.route('new');
});
});
My PostsNewRoute creates a new record in the model hook:
App.PostsNewRoute = Ember.Route.extend({
model: function() {
return this.store.createRecord('post');
}
});
Since I don't want transient record to be visible, I filter them out in my PostsRoute:
App.PostsRoute = Ember.Route.extend({
model: function() {
this.store.find('post');
return this.store.filter('post', function(post) {
return !post.get('isNew');
});
}
});
This works as expected, but every transition to posts.new add a new record to the store, which is something I would like to avoid. So, instead of calling createRecord every time the model hook is called, I filter the store for an empty record and return this if there is one found:
App.PostsNewRoute = Ember.Route.extend({
model: function() {
var route = this;
return this.store.filter('post', function(post) {
return post.get('isNew');
}).then(function(result) {
return result.get('firstObject') || route.store.createRecord('post');
);
});
This gives me at the most one empty record.
My question: is there a better way to avoid my store being populated with (many) empty records ?
UPDATE:
Instead of filtering on the isNew attribute, I can probably use currentModel:
model: function() {
this.get('currentModel') || this.store.createRecord('post');
};
You can use this addon https://github.com/dockyard/ember-data-route to clean up when you leave a /new route. It hooks into the willTransition action hook that gets called on the route whenever a transition occurs.
The source code is a short read: https://github.com/dockyard/ember-data-route/blob/master/addon/mixins/data-route.js.
The alternative would be to not create a new record in the model hook, but according to a comment of yours it doesn't seem to be an option.

Ember.js - Duplicate record in Store that clears after refresh

I have a basic Rest API and when I visit my newPost page, there is a form I can fill in with 'title' and 'text' fields as well as a submit button. After I create the record, I save it with the controller and transitionToRoute('posts').
When I view the 'posts' page, the input I just entered is displayed twice. I checked the Ember inspector and it looks like the record is put into the data store twice, once as I click the create button and one after the page refreshes. When I refresh the browser again, the duplicate is removed.
How do I get rid of the duplicate record in the store? Cheers for any suggestions.
EDIT: I believe I have solved my issue by using a filter to remove posts that hadn't been persisted yet.
Updated Routes:
App.PostsRoute = Ember.Route.extend({
model: function() {
return this.store.find('post');
},
setupController: function() {
var posts = this.store.filter('post', function (post) {
return !post.get('isDirty');
});
this.controllerFor('posts').set('model', posts);
}
});
App.NewPostRoute = Ember.Route.extend({
model: function(){
return this.store.createRecord('post');
}
});
Routes:
/* Routes */
App.Router.map(function() {
this.resource('posts');
this.resource('newPost');
});
App.PostsRoute = Ember.Route.extend({
model: function() {
return this.store.find('post');
},
setupController: function() {
var posts = this.store.filter('post', function (post) {
return !post.get('isDirty');
});
this.controllerFor('posts').set('model', posts);
}
});
App.NewPostRoute = Ember.Route.extend({
model: function(){
return this.store.createRecord('post');
}
});
NewPostController:
actions:
createPost: function() {
...
var post = this.get('store').createRecord('post', {
title: title,
text: text
});
this.set('title', '');
this.set('text', '');
post.save();
this.transitionToRoute('posts');
...
newRecord: function() {
this.set('content', App.Post);
}
Thanks!

Empty controller blocks view. Why?

I have been experimenting with using Ember with a JSON server but without without ember-data. My test app renders a directory of images from a small JSON structure (generated from a little Go server).
Can anyone explain to me why, if I uncomment the App.FileController in the code below, the corresponding File view fails to render?
window.App = Ember.Application.create();
App.Router.map(function() {
this.resource('files',function(){
this.resource('file',{path:':file_id'});
});
});
App.FilesRoute = Ember.Route.extend({
model: function() {
return App.File.findAll();
}
});
App.FileRoute = Ember.Route.extend({
setupController: function(controller, args) {
controller.set('model', App.File.find(args.id));
},
model: function(args) {
return App.File.find(args.file_id);
}
});
App.File = Ember.Object.extend({
urlPath: function(){
return "/pics/" + this.get('id');
}.property('id'),
});
If I uncomment this, things break:
// App.FileController = Ember.Controller.extend({
// });
(namely, the File sub-view no longer renders at all.)
App.File.reopenClass({
find: function(id){
file = App.File.create({'id':id});
return file;
},
findAll: function() {
return $.getJSON("http://localhost:8080/api/").then(
function(response) {
var files = [];
response.Files.forEach(function (filename) {
files.push(App.File.create({'id':filename}));
});
return files;
}
);
},
});
Also, is there something fundamental that I'm doing wrong here?
As noted by Finn MacCool and agmcleod I was trying to use the wrong type of controller. The correct lines should be:
App.FileController = Ember.ObjectController.extend({
});
Not that I need to explicitly set a FileController in this small example. However, should I go on to expand the code I will no doubt need one and will need to use the correct one.