Empty controller blocks view. Why? - ember.js

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.

Related

Ember and Yeoman error on edit controller

Following this post, on creating an workflow with Yeoman and Ember, when I hit the
/#/story/new url I get this error:
Error while loading route: Getbookmarks.StoryEditRoute<.model#http://localhost:9000/scripts/combined-scripts.js:117
superWrapper#http://localhost:9000/bower_components/ember/ember.js:1230
getModel#http://localhost:9000/bower_components/ember/ember.js:33281
model#http://localhost:9000/bower_components/ember/ember.js:33209
invokeCallback#http://localhost:9000/bower_components/ember/ember.js:9427
publish#http://localhost:9000/bower_components/ember/ember.js:9097
publishFulfillment#http://localhost:9000/bower_components/ember/ember.js:9517
DeferredActionQueues.prototype.flush#http://localhost:9000/bower_components/ember/ember.js:5650
Backburner.prototype.end#http://localhost:9000/bower_components/ember/ember.js:5741
Backburner.prototype.run#http://localhost:9000/bower_components/ember/ember.js:5780
Ember.run#http://localhost:9000/bower_components/ember/ember.js:6181
runInitialize#http://localhost:9000/bower_components/ember/ember.js:38453
jQuery.Callbacks/fire#http://localhost:9000/bower_components/jquery/jquery.js:2913
jQuery.Callbacks/self.fireWith#http://localhost:9000/bower_components/jquery/jquery.js:3025
.ready#http://localhost:9000/bower_components/jquery/jquery.js:398
completed#http://localhost:9000/bower_components/jquery/jquery.js:93
http://localhost:9000/bower_components/ember-data/ember-data.js
Line 3285
The detail of the error is:
error(error=TypeError: this.modelFor(...) is undefined
return this.get('store').find('story', this.modelFor('story').id);
, transition=Object { router={...}, promise=Promise, data={...}, more...}, originRoute=<Getbookmarks.StoryEditRoute:ember265> { routeName="story_edit", router=<Getbookmarks.Router:ember266>, store=<Getbookmarks.Store:ember267>, more...})ember.js (line 33949)
triggerEvent(handlerInfos=[Object { isDynamic=false, name="application", handler=<Getbookmarks.ApplicationRoute:ember268>, more...}, Object { isDynamic=false, name="story_edit", handler=<Getbookmarks.StoryEditRoute:ember265>}], ignoreFailure=true, args=[
TypeError: this.modelFor(...) is undefined
I don't have idea why I getting this. I've followed the tutyorial and I haven't skipped anything. My concerns is that the version of the tutorial is somewhat old, but the code is generated from the ember generator.
Any idea?
EDIT:
this is the generated code for the model:
Getbookmarks.StoryEditRoute = Ember.Route.extend({
model: function(params) {
return this.get('store').find('story', this.modelFor('story').id);
},
setupController: function(controller, model){
controller.set('model', model);
buffer = model.get('attributes').map(function(attr){
return { key: attr.get('key'), value: attr.get('value') }
});
controller.set('buffer', buffer)
}
});
Route:
Getbookmarks.Router.map(function () {
this.resource('index',{path : '/'});
this.resource('story', { path: '/story/:story_id' });
this.resource('story_edit', { path: '/story/new' });
});
I hit the same problem yesterday, following the same guide. This seems to be a problem with the routes, as story edit expects a story_id, which we can't give it for a new story. I managed to solve this problem like this, adding a new route for story_new without the story_id:
in routes.js:
this.resource('story_edit', { path: '/story/edit/:story_id' });
this.resource('story_new', { path: '/story/new' });
Then empty StoryEditController (else the app will complain about the "needs: 'story'", besides it will be replaced later anyway) and add this in routes/story_new_route.js:
Getbookmarks.StoryNewRoute = Ember.Route.extend({
renderTemplate: function() {
var controller = this.controllerFor('storyEdit');
this.render('storyEdit', { controller: controller } );
}
});
This new controller will just redirect to storyEdit controller and use storyEdit-Template. Then empty story_edit_route.js (we'll go with the defaults for now) and you app should run.
Hope this works for you, too, otherwise just tell me and I can give more details.

Multiple controllers for one view - request for the model not set

I got the following code:
App.ApplicationSerializer = DS.ActiveModelSerializer.extend({});
App.Router.map(function(){
this.resource('clients', { path : '/' });
});
App.ApplicationAdapter = DS.RESTAdapter.extend({
namespace: 'api'
});
App.ClientsRoute = Ember.Route.extend({
setupController: function(controller, model){
controller.set('model', model);
this.controllerFor('patients').set('model', this.store.find('patient'));
}
});
When the main page is loaded a request is sent only to localhost:3000/api/patients and not to clients which is the main controller for the given view :/
Can you spot the mistake? I am using App.ApplicationSerializer = DS.ActiveModelSerializer.extend({});
I thought that might be the error, but after removing it I saw no changes at all.
You are not defining the model for ClientsRoute:
App.ClientsRoute = Ember.Route.extend({
model: function() {
return this.store.find('client');
}
});
The only case where its not necessary to define the model, is when the route is a simple dynamic segment (show a specific record). Example:
App.Router.map(function() {
this.route('client', { path: '/clients/:client_id' });
});
App.ClientRoute = Ember.Route.extend({
// Default model (no need to explicitly define it):
// model: function(params) {
// return this.store.find('client', params.client_id);
// }
});

Load model like a refresh without ember-data

I'm writing a little ember app without using Ember-Data (using TheMovieDB API) and I don't understand why model is not load when I click on a {{#linkTo}} link, but when I refresh the page manually datas are loaded correctly.
Here is my App.js :
window.App = Ember.Application.create();
App.Router.map(function() {
this.route('about');
this.resource('movie', {
path: '/movie/:movie_id'
})
});
App.IndexRoute = Ember.Route.extend({
setupController: function (controller) {
var movies = [];
$.ajax({
url: "http://api.themoviedb.org/3/movie/popular?api_key=5b088f4b0e39fa8bc5c9d015d9706547",
type: "GET",
async: false,
success: function (data) {
var length = data.results.length;
data.results.forEach(function (item) {
if (item.backdrop_path != null) {
var tmp = item.backdrop_path;
item.backdrop_path = "http://cf2.imgobject.com/t/p/w500/"+tmp+"?api_key=5b088f4b0e39fa8bc5c9d015d9706547"
movies.push(item);
}
})
}
});
controller.set('content', movies);
}
});
App.MovieRoute = Ember.Route.extend({
model: function (param) {
var infos;
/* Important !! */
var promise = Ember.Deferred.create();
$.ajax({
url: "http://api.themoviedb.org/3/movie/"+param.movie_id+"?api_key=5b088f4b0e39fa8bc5c9d015d9706547",
type: "GET",
success: function (data) {
var tmp = data.backdrop_path;
data.backdrop_path = "http://cf2.imgobject.com/t/p/w500/"+tmp+"?api_key=5b088f4b0e39fa8bc5c9d015d9706547";
// infos = Ember.Object.create(data)
promise.resolve(data);
}
});
console.log("MODEL");
return promise;
},
setupController: function (controller, model) {
controller.set('content', model);
}
});
App.Movie = Ember.Object.extend({})
Thanks for your help !
Since you have not specified which model you mean, I'm assuming you mean the movie model, and with my assumption I'm trying to answer.
I think your problem is that your template expects the model coming from a MovieIndexController because you specified a resource in your router map instead of a simple route.
That said, the solution might be to rename your controller to MovieIndexController and respectively the route MovieIndexRoute.
Here the reference my answer is based on, under the paragraph Resources.
Hope it helps

emberjs controller's needs returns undefined and unable to access controller.content

I am trying to use Emberjs needs api* to access Postscontroller from a comments controller. The PostController is backed by a route while I don't want the comment's controller to have a route.
In the comments controller, I have needs: ['posts', 'postsShow']. From the comments controller, when I run console log with the following commands,:
console.log( this.get('controllers.postsShow') );
console.log( this.get('controllers.posts') );
In the console I see:
<EmBlog.PostsShowController:ember396> { target=<EmBlog.Router:ember316>, namespace=EmBlog, store=<EmBlog.Store:ember336>
<EmBlog.PostsController:ember304> { target=<EmBlog.Router:ember316>, namespace=EmBlog, store=<EmBlog.Store:ember336>
However, when I try to access the controller content for PostsShowController or PostsController, it always returns post undefined. These are various approaches I have tried and still got post undefined:
var post = this.get('controllers.posts').get('content');
or
var post = this.get('controllers.posts.content');
Also I tried to get 'comments' from the content like this:
var post = this.get('controllers.posts')
var comment = post.get('comments');
or
comment = post.comments;
I still got the error:
TypeError: post is undefined comment = post.comments;
TypeError: post is undefined var comment = post.get('comments');
Which also means:
var post = this.get('controllers.posts.model').get('store.transaction');
also returns post is undefined.
This is the jsfiddle and the relevant section of the code is pasted below:
EmBlog.PostsNewController = Ember.ObjectController.extend({
content: null
});
EmBlog.PostsShowController =
Ember.ObjectController.extend({
content: null
});
EmBlog.CommentNewController = Em.ObjectController.extend({
needs: ['posts', 'postsShow'],
isAddingNew: false,
addComment: function(body){
console.log( this.get('controllers.postsShow') );
console.log( this.get('controllers.posts') );
var post = this.get('controllers.posts.content');
store = post.get('store.transaction');
}
});
Many thanks
That's because posts controller is empty. You are filling the posts in PostIndexController, not PostsController.
Check the route:
EmBlog.PostsRoute = Ember.Route.extend({
});
EmBlog.PostsIndexRoute = Ember.Route.extend({
model: function(){
return EmBlog.Post.find();
},
setupController: function(controller, model){
controller.set('content', model);
}
});
So you should either do
needs: ['postsIndex', 'postsShow']
and then:
this.get('controllers.postsIndex.content')
or fix your route:
EmBlog.PostsRoute = Ember.Route.extend({
model: function() {
return EmBlog.Post.find();
}
});
EmBlog.PostsIndexRoute = Ember.Route.extend({
model: function(){
return this.modelFor('posts');
},
setupController: function(controller, model){
controller.set('content', model);
}
});
Updated fiddle

Redirecting to first item in ArrayController

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