How do you configure a root route in Ember.js - ember.js

I would like to create a route for / that loads another route, say 'posts'. It seems that the only two solutions are to configure Ember's IndexRoute:
App.IndexRoute = Ember.Route.extend({
redirect: function() {
return this.transitionTo('posts');
}
});
OR
Map our 'posts' resource to the / path:
App.Router.map(function() {
return this.resource('posts', { path: '/' });
});
The first solution does not seem reasonable because it always sends visitors to /posts instead of having an actual base path of /. The second solution does not seem reasonable because it only allows posts to be viewed from / and not /posts. The second solution inherently creates strange nested URLs like /new for a new post instead of /posts/new.
What is the most idiomatic way to configure / to load another route instead of redirecting, while still making the target resource available from its normal URL? In other words, I would like the / path to access posts, and still have posts available via /posts.

Another way to go is to have your IndexController needs the PostsController, and then you can use render in your index template to render the posts.
App.IndexController = Ember.Controller.extend({
needs : ["posts"]
});
And then your index template might just be
{{render 'posts'}}

I think what you want to do is the following:
App.IndexRoute = Ember.Route.extend({
model: function() {
return this.get('store').findAll('post');
},
setupController: function(controller, model) {
this.controllerFor('posts').set('content', model);
}
});
That way the controller for this route will be an ArrayController filled with all your posts. And you can still use your /posts route whichever way you like. By default this would be App.IndexController (which you can override to implement custom functionality).
Alternatively, if you wanted to use a different controller (say App.PostsController), you could specify that in the routes renderTemplate hook. So if you wanted to use your posts template and your App.PostsController used in your App.IndexRoute, you would include:
renderTemplate: function() {
this.render('posts', { controller: 'posts' });
}
For more details have a look at the routing section of the Ember.js guides.

Related

EmberJS redirect when no subroute specified

I have a set of nested routes and templates that I'd like to auto-select the first model if no sub-routes are specified. The route structure is:
App.Router.map(function() {
this.route('sales', function () {
this.route('orders', function () {
this.route('order', { path: ':order_id' });
});
});
});
If the user hits sales.orders then they should be redirected to the first order on the sales.orders model. Making this work is no problem. The issue comes when the user hits sales/orders/:order_id No matter what :order_id is the user is always redirected to the first order in the orders array.
I'm currently performing the redirect in the setupControllerhook of the SalesOrders route as I have some sorting on the controller that needs to be in place prior to redirecting.
App.SalesOrdersRoute = Ember.Route.extend({
model: function () {
return this.store.find('order');
},
setupController: function (controller, model) {
controller.set('model', model);
var firstObject = controller.get('sortedContent').get('firstObject');
this.transitionTo('sales.orders.order', firstObject);
}
});
App.SalesOrdersController = Ember.ArrayController.extend({
sortProperties: ['orderNumber:desc'],
sortedContent: Ember.computed.sort('model', 'sortProperties')
});
I have a jsbin that shows my issue.
Hitting any specific order will always redirect to the first order in the array (4 in this case).
I need it to keep the deep linked url and only redirect when no order is specified.
I feel like this question and this question are both similar to what I'm trying to do except neither addresses auto-selecting the first item if no sub-routes are specified.
You should do the redirect in your SalesOrdersIndex route. The additional index route of each route will only be created when it matches the complete URL mapping. So for any url that isn't exactly "sales/orders" it will not be created. Just what you want.
App.SalesOrdersIndexRoute = Ember.Route.extend({
setupController: function (controller, model) {
controller.set('model', model);
this.controllerFor('salesOrders').set('model',model);
var firstObject = this.controllerFor('salesOrders').get('sortedContent').get('firstObject');
this.transitionTo('sales.orders.order', firstObject);
}
});
jsbin: 4 3 2 1 redirect to 4
One option could be to check the transition on the afterModel hook and redirect if the user is trying to access the sales.orders.index route.
Something like this:
afterModel: function(model, transition){
if (transition.targetName === "sales.orders.index"){
var first = model.objectAt(0);
this.transitionTo('sales.orders.order', first);
}
}
Here's an example.
That won't work with setupController as the setupController hook does not have access to the transition. In your case, since you just want to sort, you could do something like:
var first = model.sortBy('orderNumber').reverse().objectAt(0);
As far as I know, setupController is called after both redirect and afterModel so I'm not sure it's possible to get the sorted content from the controller through the afterModel and redirect hooks.

How to access dynamic segment in a controller in Ember.js?

I have an example route:
this.route('client', {path: ':id'});
I can access this in my route like this:
model: function(params) {
console.log(params.id);
}
How do I access the :id in my controller?
This is how I do it in my application. Not sure if this is the best approach.
App.IndexRoute = Em.Route.extend({
model: function(params) {
this.set('params', params);
},
setupController: function(controller, model) {
controller.set('params', this.get('params'));
this._super(controller, model);
}
});
Alternatively you can also do a lookup on the container inside your controller. But I dont really think this is a good approach. Here is an example.
this.get('container').lookup('router:main').router.currentHandlerInfos
.findBy('name','index').params
There's a serialize function within the Route that you can take advantage of. Here's the API Documentation for it. However, in your context, you can just do this:
App.IndexRoute = Em.Route.extend({
model: function(params) {
console.log(params.client_id);
},
serialize: function(model){
return { client_id : model.id };
}
});
Let me know if this works!
I also noticed an error in your route definition. It should presumably be
this.route('client', {path: '/:client_id'});
or
this.route('client', {path: '/client/:client_id'});
If your id happens to be part of your model you can retrieve from the model itself.
For example, if you have a route with an object Bill as a model, and the path bills/:billId, on your controller you can retrieve it this way:
this.get('model').id
I just came across this question since I too was wondering what was the best way to do this. I chose to go via the route of returning a hash from the model rather than setting parameters in the route.
So in your case I would do the following:
model() {
Ember.RSVP.hash({client: <get client>, id: id});
}
And then in the controller or template I can access the client by calling
model.client
and get the id by calling
model.id
I personally feel this is a cleaner way of accessing the id as compared to setting a param on the route. Of course I am assuming that the id is not already set on the model. Otherwise this entire exercise is pointless.

EmberJS get dynamic parameter in nested route

I am developing a website using Ember JS.
I have created a nested route like this:
//router
this.resource('store/checkout', {path: '/store/checkout/:order_id'}, function(){
this.resource('store/checkout-lines', {path: ''});
});
This results in the route /store/checkout/:order_id calling both routes and corresponding tempaltes.
The template for store/checkout has an {{outlet}} for the template store/checkout-lines.
In the routes I have this code:
//store/chekout
export default Ember.Route.extend({
model: function(params) {
return this.store.find('order', params.order_id);
}
});
//store/checkout-lines
export default Ember.Route.extend({
model: function(params) {
var order_id = params.order_id; //this does not work
return this.store.find('order-item', {orderId: order_id});
}
});
But my problem is that in the route for store/checkout-lines, I cannot get the orderId.
How can I achieve this? Or am I at the wrong track and should be doing this in another way?
My goal is that the route /store/checkout/:order_id should call the server to fetch both order and orderItems.
What some people seem to miss is that even if you are visiting a nested route, the model for the parent route is loaded. In your nested route, you can easily fetch the model from the parent route using modelFor(type)and then get your information from there. In your case it would be like this.
//store/checkout-lines
export default Ember.Route.extend({
model: function(params) {
var order_id = this.modelFor('checkout').get('id');
return this.store.find('order-item', { orderId: order_id });
}
});
This might seem like an extra step but when you get around to it it really makes a lot of sense and works very well.

Find query concept for route and controller

my question is a little bit general. What is the best concept for route and controller with findQuery in ember.
I have api with data filtering. Data request is executed by
this.store.findQuery('dataModel', {"q": JSON.stringify({"filters": filters})});
after that I show them in table view. The filter is updated by form views in a template.
My current solution:
Form views set controller parameters and a button call action from controller. Controller action loads parameter, executes findQuery and set('content',data).
In most cases I saw concept with a defining model: function() .. in the Route and setupController: function(controller, model) with controller.set('content',model). I like this "set" because 'content' is RecordArray (not PromiseArray) and I can easily use that for datatables and another JavaScript plugins. I think my solution isn't good.
I think your concept is correct, I have been using the following flow:
In your router:
App.Router.map(function() {
this.resource('search', { path: '/query/:filters' });
});
App.SearchRoute = Ember.Route.extend({
model: function(params) {
return this.store.findQuery('dataModel', {"q": JSON.stringify({"filters": params.filters})});
});
In your html, just bind the action which will lead to the new Search Route,
something like below :
<button {{action "doSearch"}}>Search</button>
In your controller:
App.SearchController = Ember.ArrayController.extend({
...
actions: {
doSearch: function() {
var query = buildYourQueryObject();
this.transitionToRoute("search", query);
}
}
Upon clicking on the button, the app will transition into your search route, and "query" will be serialized and sent into the Route, and the Route.model() will attempt to be populated based on the serialized parameters provided.
Note: The code has been simplified, you might need to add more stuff in order to make it work

How to save path and return to it with Ember's V2 Router

So, I'm having some issues with Ember's new router. I'm trying to save and later return to the current path for a given dynamic segment, so my urls might look like
#/inventory/vehicle/1001
Which can then branch off into
#/inventory/vehicle/1001/details
#/inventory/vehicle/1001/photos
#/inventory/vehicle/1001/description
etc. I need a way to return to the most recent route. The Ember guides have a method for this here:
http://emberjs.com/guides/routing/redirection/
The problem with this method is that by creating the "/choose" route and assigning it to "/", this overwrites the standard "/inventory/vehicle/1001" route. For instance, if I were to try to create a linkTo a vehicle like so:
{{#linkTo "vehicle" vehicle}}
Then Ember will throw an error because the "vehicle" route no longer exists. Instead, it must be set to:
{{#linkTo "vehicle.choose" vehicle}}
Which works, activates the VehicleChooseRoute and everything. Except, since "vehicle.choose" is technically a child of "vehicle", the #linkTo ONLY has an active class applied when the current route is
#/inventory/vehicle/1001
Which instantaneously redirects to the latest filter, and so it's basically never on. So basically I'm trying to figure out a way around this. I tried changing the path of "vehicle.choose" to be the standard path (#/inventory/vehicle/1001/choose) so it doesn't overwrite the "vehicle" route, and then setting up VehicleRoute like so:
Case.Router.map(function() {
this.resource('inventory', function(){
this.route('review');
this.route('sheets');
this.resource('vehicle', { path: '/vehicle/:vehicle_id' }, function(){
this.route('choose');
this.route('details');
this.route('consignor');
this.route('additional');
this.route('price');
this.route('dmv');
this.route('expenses');
this.route('description');
this.route('tasks');
});
});
});
App.VehicleRoute = Ember.Route.extend({
model: function(params) {
return Case.Vehicle.find(params.vehicle_id);
},
setupController: function(controller, model) {
model.set('active', true);
},
redirect: function() {
this.transitionTo('vehicle.choose');
}
})
Case.VehicleChooseRoute = Ember.Route.extend({
redirect: function() {
var lastFilter = this.controllerFor('vehicle').get('lastFilter');
this.transitionTo('vehicle.' + (lastFilter || 'details'));
}
});
But the problem that arises from this (aside from feeling rather hacked together) is that redirect replaces the entire template that would normally be rendered by "vehicle" so I only get the subview. So that's not an option.