Go to new item route after creation in Emberjs - ember.js

I've something like this in my route:
actions: {
save(title, description) {
const newCard = this.get('store').createRecord('card', { title, description } );
newCard.save().then((card) => {
// card.id contains the id of the new card
this.transitionTo('cards.all'); // problem with this line
});
}
}
I want to go to the new card route after creating it. I can get the id of the new item from card.id. I've tried the following but of no use:
this.transitionTo('cards.card', card.id);
This throws some error since the cards.card route can't find the id in the params.
The following:
this.transitionTo('cards/' + card.id);
throws some error stating that cards/45 route not found but if the new item is created and I can navigate to it.
I'm using Ember 2.6.1.
EDIT
My router.js file:
this.route('cards', function() {
this.route('all');
this.route('card', {path: '/:card_id'}, function() {
this.route('edit');
});
this.route('new');
});

Is this what your router currently looks like?
this.route('cards', function() {
this.route('all');
this.route('card', {path: '/:card_id'}, function() {
this.route('edit');
});
this.route('new');
});
If your wanting to go to the new route for a specific card then you would need to define a slug for new. Something like
this.route('new', {path: /:card_id});
The transition in your action hash would like like this:
this.transitionTo('new', card.id);
Hopefully I'm understanding your use case correctly.

It should be this:
this.transitionTo('cards.card', card);
I only needed to do this since my card.js was expecting params object and I was trying to pass a number:
model(params) {
return this.store.find('card', params.id);
}

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.

EmberJS dynamic routing and configuration

I use EmberJS v1.0.0-rc.2 and requireJS.
My app structure is kinda like that.
- app
- - moduleA
My main file:
#app/main.js
var App = Ember.Application.create({
VERSION: '1.0',
//Log router transitions:
LOG_TRANSITIONS: true
});
App.Router.map(function() {
AppRouting.call(this);
});
App = AppRoutingExtend(App);
App.initialize();
Functions AppRouting() and AppRoutingExtend() could be find in the fellow file:
#app/routing.js
function AppRouting() {
this.resource('root', {path: '/'}, function() {
this.resource('moduleA', {path: '/'}, function() {
ModuleARouting.call(this);
});
});
}
function AppRoutingExtend(App) {
var ModuleARouting = ModuleARoutingExtend();
//Check if ModuleARouting is not empty
if (!Ember.isEmpty(ModuleARouting)) {
$.each(ModuleARouting, function(key, value) {
//Check if key is a string of only letters
// And if value is like Ember.Route.extend({})
if (typeof key === 'string' && /^[a-zA-Z]+$/.test(key)
&& value.toString() == '(subclass of Ember.Route)') {
eval("App.Root" + "ModuleA" + key + "Route = value");
} else {
//Throw error
}
});
}
return App;
}
Functions ModuleARouting() & ModuleARoutingExtend() could be find in the follow file:
#app/moduleA/routing.js
function ModuleARouting() {
this.route("contributors", {path: "/"});
this.resource('aContributor', {path: "/:githubUserName"}, function() {
this.route("details", {path: "/"});
this.route("repos", {path: "/repos"});
});
}
function ModuleARoutingExtend() {
var routes = {};
routes['Contributors'] = Ember.Route.extend({
/*
Some Code
*/
});
routes['AContributor'] = Ember.Route.extend({
/*
Some Code
*/
});
routes['AContributorDetails'] = Ember.Route.extend({
/*
Some Code
*/
});
routes['AContributorRepos'] = Ember.Route.extend({
/*
Some Code
*/
});
return routes;
}
I created AppRouting() and ModuleARouting() to be able to add dynamically some routing path to my application, by adding a new module or remove one. By this way, each module could have its intern structure and AppRouting() just merge them.
However, I'm not sure about ModuleARoutingExtend() and more specifically AppRoutingExtend(). In the last one, I try to modify routes like App.RootModuleAContributorsRoute. By the way I don't have information directly which routes have been created by the ModuleARouting.call(this), I cannot know the name of the variable RootModuleAContributorsRoute. This is the reason I use eval to be dynamic by getting the 'Contributors' from ModuleARoutingExtend() and its value, Ember.Route.extend({/* some code *});
So, my question is: Is there a better way to add dynamically some routes for my application and to get their configuration? And if not, is it still a good way?

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.

How to access a parent model within a nested index route using ember.js?

I have the following route structure
App.Router.map(function(match) {
this.route("days", { path: "/" });
this.resource("day", { path: "/:day_id" }, function() {
this.resource("appointment", { path: "/appointment" }, function() {
this.route("edit", { path: "/edit" });
});
});
});
When I'm inside the AppointmentIndexRoute I'm looking for a way to create a new model using some meta day from the day (parent) model but because the day model does not yet know about this appointment I'm unsure how to associate them until the appointment is created / and the commit is fired off.
Any help would be much appreciated
From within the AppointmentIndexRoute's model hook you can use modelFor('day') to access the parent model. For example:
App.AppointmentIndexRoute = Ember.Route.extend({
model: function(params) {
day = this.modelFor("day");
...
}
});
Another example is here: emberjs 1.0.0pre4 how do you pass a context object to a resource "...Index" route?
What if I am not using ember data? How do I get the parent id in a route like
this.resource('workspace',function () {
this.resource('workflow', {path: '/:workspace_id/workflow'}, function () {
this.route('show', {path: '/:workflow_id'});
});
});
This code will not work:
App.WorkflowShowRoute = Em.Route.extend({
model: function(params) {
var ws = this.modelFor('workspace'); //ws is undefined
return this.store.find('workflow', params.id, ws.id);
}
});
EDIT:
I found a workaround, it's not ideal but works exactly the way I want it.
this.resource('workspace',function () {
this.route('new');
this.route('show', {path: '/:workspace_id'});
//workflow routes
this.resource('workflow', {path: '/'}, function () {
this.route('new', {path:'/:workspace_id/workflow/new'});
this.route('show', {path: '/:workspace_id/workflow/:workflow_id'});
});
});
And in my workflow route, I can access the workspace_id jus as I expect from the params property:
App.WorkflowShowRoute = Em.Route.extend({
model: function(params) {
return this.store.find('workflow', params.workflow_id, params.workspace_id);
}
});
Finally, here is my link-to inside the workspace.show route helper:
{{#each workflow in workflows}}
<li>
{{#link-to 'workflow.show' this.id workflow.id}}{{workflow.name}}{{/link-to}}
</li>
{{/each}}

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.