why am i seeing "error while loading route" with ember? - ember.js

ember rc1, ember-data rev 12. all my other routes load correctly, unsure why i'm seeing this error. it happens when i try to access the show route i.e. /files/groups/5. the index route renders fine.
i've pasted the stack trace below but it's not very informative. is there something basically wrong i'm doing here?
my routes/controllers are set up as follows:
this.resource('files', { path : '/files' }, function() {
this.resource('groups', { path : '/groups' }, function() {
this.route('show', { path : '/:asset_link_group_id' });
});
});
AssetLinksApp.GroupsShowController = Ember.ArrayController.extend({
content : Ember.A(),
assetLinkGroup : null
});
AssetLinksApp.GroupsShowRoute = AssetLinksApp.AuthRequiredRoute.extend({
setupController : function(controller,model) {
controller.set('content',model.get('asset_links'));
controller.set('assetLinkGroup',model);
},
model : function(params) {
return AssetLinksApp.AssetLinkGroup.find(params.asset_link_group_id);
}
});
the stack trace:
Error while loading route: TypeError {} exchange_vendor.js:12078
(anonymous function) exchange_vendor.js:12078
Ember.Router.reopenClass.defaultFailureHandler.setup
exchange_vendor.js:35011 failure exchange_vendor.js:34448
objects.concat.context exchange_vendor.js:34497 invokeCallback
exchange_vendor.js:17846 Promise.then exchange_vendor.js:17893
EventTarget.trigger exchange_vendor.js:17822 results
exchange_vendor.js:17924 RunLoop._prev exchange_vendor.js:15911
Ember.handleErrors exchange_vendor.js:12140 invoke
exchange_vendor.js:15909 iter exchange_vendor.js:15981 RunLoop.flush
exchange_vendor.js:16035 RunLoop.end exchange_vendor.js:15940 tryable
exchange_vendor.js:16143 Ember.tryFinally exchange_vendor.js:12831
Ember.run.end exchange_vendor.js:16146 Ember.tryFinally
exchange_vendor.js:12833 Ember.run exchange_vendor.js:16102
Ember.HashLocation.Ember.Object.extend.onUpdateURL
exchange_vendor.js:36690 jQuery.event.dispatch exchange_vendor.js:3144
jQuery.event.add.elemData.handle.eventHandle

The model returns a single record. However you have defined an ArrayController.
Ember.js automatically places the model in the controller's content property, which will cause an error since it will be putting a single record in an array controller.
Even though you overrode the setupController, before it fires, Ember.js will place the model in the controller anyway. There's currently no way of stopping that.
Update 3-June-2014 (Ember > 1.0) :
If you now override setupController Ember no longer sets the model property.
The only solution I can think of is to add a resource to your routes:
this.resource('files', { path : '/files' }, function() {
this.resource('groups', { path : '/groups' }, function() {
this.resource('group', { path : '/:asset_link_group_id' }, function() {
this.route('index');
});
});
});
This means that you have an object controller (GroupController) containing the group,
and an array controller (GroupIndexController) containing the asset links array.
AssetLinksApp.GroupIndexController = Ember.ArrayController.extend({
assetLinkGroup : null
});
AssetLinksApp.GroupIndexRoute = AssetLinksApp.AuthRequiredRoute.extend({
setupController : function(controller,model) {
controller.set('content',model);
controller.set('assetLinkGroup', this.modelFor('group'));
},
model : function(params) {
return this.modelFor('group').get('asset_links');
}
});
Now your template should be named group/index instead of groups/show. As for group template, it can be an empty template containing {{outlet}}.
Most important thing to note from this is: If your controller is an object controller, you have to return an object from the model hook, and if your controller is an array controller, then you have to return an array from the model hook.
The bright side is, this pushes you to follow a certain design that I and probably the core team think is better.

Related

Ember: Unable to get model in controller init function

I have a ember application whose route, controller are as follows:
//Route
model:function(){
return this.store.getJSON('object/');
},
setupController:function(controller, model){
controller.set('content', model)
}
//controller
init:function(){
this.myfunction();
},
myfunction:function(){
var content = this.get('content')
console.log(content.length)
}
myfunction gets executed when the page/template is loaded and it returns "cannot read property of null" . I am unable to get route model in controller init function.
The problem is that this is not referring to the the controller because of the way you called myfunction. In your controller, myfunction should be an action:
init() {
this.send('myfunction');
},
actions: {
myfunction: function() {
var content = this.get('content');
console.log(content.length);
}
}

emberjs refreshing ArrayController with arrangedContent and #each crashes

Hy and thanks for reading :
I have an issue with Ember.ArrayController and arrangedContent. The senario is as follow :
Items inside my arrayController can be modified by some actions.
My arrangedContent is filtred on some of those items properties.
Thus if an observed item property change the arrangedContent property should be refreshed
I achieve this by setting the property() of my arrangedContent with "content.#each.myproperty"
All works fine, except if i try to refresh the model from the route i then get an error message TypeError: Cannot read property 'destroy' of undefined
at ContainerView.extend.arrayWillChange
In some case it would work but duplicate the content every time the refresh() is triggered
with some minimal code it may become more clear ...
App.IndexRoute = Ember.Route.extend({
model : function(){
return [App.Cars.create({color : "red", model : "march"}),
App.Cars.create({color : "yellow", model : "alto"}),
App.Cars.create({color : "blue", model : "gundam"}) ];
},
actions : {
reload : function(){
this.refresh();
}
}
});
App.IndexController = Ember.ArrayController.extend({
arrangedContent : function(){
var data= this.get("content");
data=data.filter(function(elem){
return elem.get("color").match(new RegExp("el","gi"))!==null;
});
return data;
}.property("lenght","content.#each.color"),
actions : {
allYell :function(){
this.get("content").forEach(function(elem){
elem.set("color","yellow");
});
},
erase : function(){
if(this.get("length")>0){
this.get("content").removeAt(0);
}
}
}
});
a JSBin can be found here http://jsbin.com/yebopobetu/3/edit?html,js,console,output
I've never seen anyone recommend overriding arrangedContent. I honestly wouldn't recommend it.
App.IndexController = Ember.ArrayController.extend({
foos : function(){
var data= this.get("model");
return data.filter(function(elem){
return elem.get("color").match(new RegExp("el","gi"))!==null;
});
}.property("#each.color"),
actions : {
allYell :function(){
this.forEach(function(elem){
elem.set("color","yellow");
});
},
erase : function(){
if(this.get("length")>0){
this.removeAt(0);
}
}
}
});
http://jsbin.com/sivugecunu/1/edit

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.

How to manually update an ArrayController

In my Application I have the following rawNodes property, which I am using as an application-wide cache:
var App = Ember.Application.createWithMixins({
...
/**
The rawNodes property is a nodes deposit to be used
to populate combo boxes etc.
**/
rawNodes: null,
getNodes: function () {
if (!this.rawNodes) {
this.rawNodes = this.Node.find();
}
},
...
});
In some of my controllers I am modifying data which should also be updated in this generic cache. I would like to implement a couple of functions, to update a given node, and to delete a given node. Something like:
updateNode: function(node_id, node) {
this.rawNodes.update(node_id, node);
},
deleteNode: function(node_id) {
this.rawNodes.delete(node_id);
}
But I do not really know how to work with an ArrayController, not even if those operations are at all possible. I see no examples of this kind of procedures in the ArrayController documentation. Could somebody offer an example, or point me in the right direction?
Rather than using a rawNodes property, I think it might be more useful to
maintain a Node model and a NodesController. Assign the model property
with setupController so you can be sure that nodes are always fetched.
Since this is an application-wide cache, use needs in ApplicationController so it can delegate to its methods.
App.ApplicationRoute = Em.Route.extend({
setupController: function() {
this.controllerFor("nodes").set("model", App.Node.find());
}
});
App.ApplicationController = Em.Controller.extend({
needs: "nodes",
});
App.NodesController = Em.ArrayController.extend({
getNodes: function() {
// ...
}
});
App.NodeController = Em.ObjectController.extend({
updateNode: function() {
// ...
},
deleteNode: function() {
// ...
}
});

Ember Router transitionTo nested route with params

App.Router.map(function() {
this.resource('documents', { path: '/documents' }, function() {
this.route('edit', { path: ':document_id/edit' });
});
this.resource('documentsFiltered', { path: '/documents/:type_id' }, function() {
this.route('edit', { path: ':document_id/edit' });
this.route('new');
});
});
And this controller with a subview event that basically transitions to a filtered document
App.DocumentsController = Ember.ArrayController.extend({
subview: function(context) {
Ember.run.next(this, function() {
//window.location.hash = '#/documents/'+context.id;
return this.transitionTo('documentsFiltered', context);
});
},
});
My problem is that this code works fine when Hash of page is changed.
But when I run the above code NOT w/ the location.hash bit and w/ the Ember native transitionTo I get a cryptic
Uncaught TypeError: Object [object Object] has no method 'slice'
Any clues?
Thanks
UPDATE:
App.DocumentsFilteredRoute = Ember.Route.extend({
model: function(params) {
return App.Document.find({type_id: params.type_id});
},
});
{{#collection contentBinding="documents" tagName="ul" class="content-nav"}}
<li {{action subview this}}>{{this.nameOfType}}</li>
{{/collection}}
The problem is that your model hook is returning an array, while in your transitionTo you are using a single object. As a rule of thumb your calls to transitionTo should pass the same data structure that is returned by your model hook. Following this rule of thumb i would recommend to do the following:
App.DocumentsController = Ember.ArrayController.extend({
subview: function(document) {
var documents = App.Document.find({type_id: document.get("typeId")});
Ember.run.next(this, function() {
return this.transitionTo('documentsFiltered', documents);
});
}
});
Note: I assume that the type_id is stored in the attribute typeId. Maybe you need to adapt it according to your needs.