How dynamically load route in EmberJS? - ember.js

Need on change search field - load route with params
/search/WORD, path of route, example, /search/:q
How is best way?
In template {{input value=str}} and in controller this.transitionToRoute('search', this.store.find...) . It require loading model in controller and duplicate to route mode:function(){ return this.store.find...}.
In controller call route by url this.transitionToRoute('/search/' + str).
Else?

why not use queryParams?
ie.,
Ember.controller.extend({
queryParams: ['search'],
search: null,
doSomethingWhenSearchChanges: function() {
// do something here...
}.property('search')
});

I would second Christopher's notion of using query params for this case. For such a solution, your route would be /search?q=WORD. This solution is much more flexible and adaptable, especially if you think your search parameters will change over time. For example, you might begin to search on a handful of different attributes, such as name, type, etc. If this occurs, your route would become /search?name=NAME&type=TYPE&etc=ETC.
If you absolutely must use a route as you've defined above, you can forward the param through the route using the model and serialize hooks. In coffeescript...
App.Router.map( ->
#route('search', {path: '/search/:q'})
)
App.SearchRoute = Ember.Route.extend({
model: (params, transition) ->
return { q: params.q }
serialize: (model) ->
return { q: model.get('q') }
})
App.SearchController = Ember.Controller.extend({
strObserver: Ember.observer( ->
#transitionToRoute('search', {q: #get('str')})
).observes('str')
})

For full refresh model need use refreshModel: true http://guides.emberjs.com/v1.11.0/routing/query-params/#toc_opting-into-a-full-transition

Related

Emberjs - need to refresh model on transtionTo

I have an Emberjs app that has a search action that needs to be available from all routes. To accomplish this, I've defined the 'search' action in the application route like this:
App.ApplicationRoute = Ember.Route.extend({
actions: {
search: function (query) {
this.transitionTo('search', { queryParams: { q: query } });
}
}
});
The 'q' querystring parameter is defined in the SearchController:
App.SearchController = Ember.ArrayController.extend({
queryParams: ['q'],
q: ''
});
The search route calls a service that queries my database with the query parameter like this:
App.SearchRoute = Ember.Route.extend({
model: function (params) {
return this.store.find('activity', { query: params.q }),
}
});
I know that the model hook is not called on transtionTo, but when the user is already on the search page and wants to search again with a different query, I need to reload the search route with a new model.
Is using transitionTo in the application route the wrong approach in this case?
Any ideas are greatly appreciated!
I would add a named {{outlet}} in your Application template, wherever you want the search results to appear. Then, in your Application route, inside the renderTemplate hook, I would render the search results template into the new outlet, also specifying what controller it should use.
On the controller, you can create a computed property, which would detect changes in the query string (or however you want to supply the search results). This property (or properties) would then feed the data in your search results template.
More on rendering a template inside a route:
http://emberjs.com/guides/routing/rendering-a-template/
If you decide to go with putting the renderTemplate hook in Application route, you can set the Search controller's model (or whatever you want to call it) property from any route which needs to update the model on the search controller for it to display proper results:
this.controllerFor('search').set('model', model);
You can also create a Mixin, which would contain the renderTemplate hook, which you can include in any route you want to do your searches from. In the hook, you could send your route's model into the controller:
renderTemplate: function(controller, model) {
this.render('search', {
into: 'search',
outlet: 'application',
controller: 'search',
model: model
});
}
Play around with some of these techniques. I'm sure I'm missing some details, but I think you can get them to work.

Ember renderTemplate relay model

Working hard on my Ember app here, and it's going along fine. However, I've run into an issue of unexpected behaviour and I'm not sure regarding the best approach to this problem.
The problem is that in a specific route, I want to render another route into another outlet. However, the other route that I render into the other outlet doesn't retain it's own model.
If I do this:
App.TestRoute = Ember.Route.extend({
model: function() {
return {
heading: "Test",
testContent: "This is test."
}
}
});
App.IndexRoute = Ember.Route.extend({
renderTemplate: function() {
this.render("test", {
outlet: "left"
});
this.render({
outlet: "right"
});
},
model: function() {
return {
heading: "Index",
indexContent: "This is index."
}
}
});
... and access the IndexRoute, I would expect the TestRoute's model to be rendered into the TestRoute's template, but only the IndexRoute's model is relayed to both templates.
Fiddle here:
http://jsfiddle.net/3TtGD/1/
How do I allow Ember to use the default model for a route without having to expressively merge them? It seems tedious.
Also, having the same name of some model properties, like {{heading}} is desirable, but not necessary.
What's the best approach for solving this issue?
Thank you for your time.
Best regards,
dimhoLt
In the renderTemplate method you're telling Ember to render a template inside an outlet but it will just default the controller to the one managing the route. Given it's the controller handling the route it makes sense that it manages all the templates within that route.
Of course you can specify a different controller using:
this.render("test", {
outlet: "left",
controller: 'test'
});
it can in turn be a controller you already instantiated (and maybe set its content):
var testController = this.controllerFor('test');
testController.set(....)
this.render("test", {
outlet: "left",
controller: testController
});
About using the model: You can call this.modelFor('test') inside the route and it will return the model of the test route (it even knows if it has already been resolved). I usually do this when I need to access the model of one of the parent routes.
I believe it makes sense to access the model of a parent route, but not so much if you're accessing the model of an unrelated route. Why don't you want to merge both models?

How do you configure a root route in 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.

How to use "needs" with nested routes / controllers in emberjs RC 2

I have a very basic route setup that allows me to first show "all" records for some object. Then if the user selects a dropdown they can filter this down using a date.
I recently upgraded to RC2 and realized that "needs" has replaced or will soon replace controllerFor.
I'm curious how I can use "needs" in the below situation where I need the nested / inner route for "records.date" to change the content for the parent "records" route when a date is selected.
What is missing from below is that inside the App.RecordsDateRoute I need to change the content of the "records" controller to be a new filter (by date this time) and everything I seem to do just dumps the handlebars template and show nothing -even when I try to use something simple like
this.controllerFor("records").set('content', App.Record.find(new Date(model.loaded)))
from within the setupController method of the RecordsDateRoute
App.Router.map(function(match) {
return this.resource("records", { path: "/" }, function() {
return this.route("date", { path: "/:date_loaded" });
});
});
App.RecordsController = Ember.ArrayController.extend({
selected: 0,
dates: Ember.computed(function() {
return App.Date.find();
}).property()
});
App.RecordsIndexRoute = Ember.Route.extend({
model: function() {
this.controllerFor("records").set("selected", 0);
return App.Record.find();
}
});
App.RecordsDateRoute = Ember.Route.extend({
model: function(params) {
//the controllerFor below seems to be working great ... but what about needs?
this.controllerFor("records").set("selected", params.date_loaded);
return App.Date.create({ loaded: params.date_loaded });
}
});
With rc2, instances of other controllers can be retrieved via "controllers.controllerName", in you case it would be this.get('controllers.records').
The "needs" declaration makes the referencing controller sort of import the reference to the other controller; in your case, the date controller would be:
App.RecordsDateRoute = Ember.Route.extend({
needs: ['records'],
model: function(params) {
this.get("controllers.records").set("selected", params.date_loaded);
return App.Date.create({ loaded: params.date_loaded });
}
});
Regarding App.Record.find(new Date(model.loaded)), find() expects an id or an object whose keys and values will be used to filter the collection of models, but you're giving it a Javascript date.
Did you mean App.Record.find(new App.Date(model.loaded)), or maybe something like App.Record.find({ loaded: model.loaded }) /* assuming it's already a Date */?
There is also an initController(controller, model) method in the route called , maybe you could use that instead of "overloading" the model() method with too many responsibilities. http://emberjs.com/api/classes/Ember.Route.html#method_setupController
I recently upgraded to RC2 and realized that "needs" has replaced or will soon replace controllerFor.
To access another controller from route hooks you should continue to use controllerFor. Controller.needs is for communication between controllers, it replaces the now deprecated use of controllerFor method on controllers. AFAIK there is no plan to deprecate controllerFor on ember Routes.
I'm curious how I can use "needs" in the below situation where I need the nested / inner route for "records.date" to change the content for the parent "records" route when a date is selected.
For this use case it would be best to stick with controllerFor. It is possible to use needs this way, by specifying that App.RecordsDateController needs = ['records'] you could access the records controller via controller.get('controllers.records') from within your route's setupController hook.
What is missing from below is that inside the App.RecordsDateRoute I need to change the content of the "records" controller to be a new filter (by date this time) and everything I seem to do just dumps the handlebars template and show nothing -even when I try to use something simple like this.controllerFor("records").set('content', App.Record.find(new Date(model.loaded))) from within the setupController method of the RecordsDateRoute
App.RecordsDateRoute = Ember.Route.extend({
model: function(params) {
return App.Date.create({ loaded: params.date_loaded });
},
setupController: function(controller, model) {
var recordsController = this.controllerFor("records");
// Moved this from model hook, since here you are 'setting up a controller'
recordsController.set("selected", model.date_loaded);
// Set query based on current route's model
var query = { loaded: model.loaded };
recordsController.set("content", App.Record.find(query));
}
});

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.