from ember.js documentation, for RC4:
LINKTO NOW GENERATES ROUTETO EVENTS
Previously, the {{linkTo}} helper caused the router to transition
without any hooks to affect that behavior. Thanks to the work of Alex
Matchneer, the {{linkTo}} helper now generates a routeTo event that
can be handled just like any other event in a controller or a route's
events object. The default behavior of transitioning to the specified
route remains unchanged.
I don't understand how I can use it. I tried to do something like this:
App.ApplicationController = Ember.Controller.extend
routeTo: -> alert "hello"
but it's never fired when i click a link.
My goal was rolling back transactions when i leave a route
This should have been in the blog post, but you need to enable a flag on the ENV object. This would work:
Ember.ENV.ENABLE_ROUTE_TO = true
That said, routeTo might be short-lived as the next iteration of the router API is fully realized. Most likely, all transitions will fire an event that can be intercepted to halt the transition, and likely the event will be called willTransition. But this is not 100% solidified, so be sure to subscribe to https://gist.github.com/machty/5647589 for the latest.
update
I also totally missed out on the other part of your question (or maybe it was updated later), but routeTo is not a property you define on the controller, but rather an event that gets fired on the routes. So instead of having it on the controller, you'd do
App.SomeRoute = Ember.Route.extend
events:
routeTo: -> alert "hello"
But you wouldn't want this on ApplicationRoute since that's where the default-installed routeTo handler lives to perform the transition for you. The whole purpose of routeTo is to catch the event on a leafier route and possibly prevent it from happening.
important caveat
Just want to reiterate that this is a very short-lived feature of the API (it was somewhat experimental anyway, hence the fact that it was wrapped in a flag). In the next iteration of the router API, routeTo will likely be replaced by a willTransition event, at which I'll update this answer.
If you want to roll back transactions when you leave a route, you can use the deactivate hook as detailed here: http://emberjs.com/api/classes/Ember.Route.html#method_deactivate
This is called just before the route exits.
Something like this:
App.MyFavouriteRoute = Ember.Route.extend({
deactivate: function() {
//roll back your transaction here
console.log('deactivating my favourite route');
}
});
Note that this isn't called if you simply change the model... ie: if you transition from /myFavourite/1 to /myFavourite/2 then deactivate won't be called.... however if you transition to /myFavourites then it will be called
Related
just imagine there are more components rendered in web page, and when i click a link, i route to home page, which fills the entire page. (means all components gone in UI)
What are all the things i should make sure to clean up to avoid memory leaks?
Few examples:
If i used Ember.addObserver in component's didInsertElement, i should remove it using Ember.removeObserver in willDestroyElement method.
And any event handlers attached should be detached.
Can i get some more examples / link to see what are all the things i should cleanup and where?
The ideal is to write your code so that no clean-up is required.
For instance, if you define an observer as
observeWhatever: Ember.observer('whatever', function() {...
no special teardown is necessary. In the same way, if you define an event handler as
click: function(event) { ...
then no tear-down is necessary. But what if you want to listen to a DOM event on some element within your component? We often see people who can't remember whether they're programming in Ember or jQuery do things like:
setupWhatever: function() {
Ember.$('.foo.bar.baz').on('click', function() { alert('baz clicked!'); });
}.on('init')
This is pretty much an anti-pattern, and not just because it will need to be torn down. It is completely jumbling up how to refer to parts of the app, and how to set up callbacks, and how to handle actions. The thing you are referring to as .foo.bar.baz should be its own component, which sets up its own listener at the component level. It would send an action, which would be handled as follows:
{{foo-bar-baz action=(action 'bazClicked')}}
actions: {
bazClicked() { alert('baz clicked'); }
...
}
If you want to set up a hook, such as
listen: Ember.on('message', function() ...
that will also be torn down for you.
If I used Ember.addObserver in component's didInsertElement, I should remove it using Ember.removeObserver in willDestroyElement method.
What kind of observer would you be setting up in didInsertElement? Excessive use of didInsertElement is also an anti-pattern. If you are using Ember correctly, it should hardly ever be necessary. A possible exception is initializing a jQuery add-on, if you are so unfortunate as to be using one of those.
If for whatever reason you are working with timers, you may need to cancel them on exit.
To explain the issue I'm facing, I have 2 routes : ApplicationRoute and SomeRoute. I would need to wait for ApplicationRoute to resolve before trying to do something with SomeRoute.
The call order I see in debug :
ApplicationRoute.model // which returns a promise
SomeRoute.model
SomeRoute.setupController
ApplicationRoute.setupController
What I would want :
ApplicationRoute.model // which returns a promise
ApplicationRoute.setupController
SomeRoute.model
SomeRoute.setupController
There's probably a detail I'm missing somewhere...
UPDATE
The reason I incist for the ApplicationRoute.setupController to be able before other routes is that I use this to initialize some others controllers.
App.ApplicationRoute = Ember.Route.extend({
model: function() {
return Ember.$.getJSON("api/settings");
},
setupController: function(controller, jsonSettings) {
this.store.pushPayload(jsonSettings);
this.controllerFor("foobar").set("content", this.store.all("foobar"));
this.controllerFor("foobaz").set("content", this.store.all("foobaz"));
this.controllerFor("foobam").set("content", this.store.all("foobam"));
}
});
For a transition to proceed, all models in the hierarchy must resolve. That's as it should be; we don't want to start doing application logic--which includes things that might be done in a setupController, at whatever level of the hierarchy--while the necessary models are still pending and the transition has not been finalized and might even still be aborted.
So if we agree that models are resolved in top-down order, which makes sense--after all, a child route's model hook might want to refer to this.modelFor('parent')--why is setupController called in reverse order, from the bottom up? I'm guessing there may be good reasons for this, including the rendering lifecycle, but in one important sense it doesn't really matter: a parent route's controller being set up properly is not a prerequisite for the child route's controller to be set up. The parent route's controller is not even directly accessible from the child route's controller unless you inject it. For more on this topic, see http://discuss.emberjs.com/t/why-is-setupcontroller-called-after-the-model-hook-of-nested-routes/2655/9.
As an aside, you use the expression "a route resolving", and seem to imply that calling setupController is a part of that resolution process, but it would be more correct to refer to "a route's model resolving", and that does not include the invocation of setupController, which is something that happens after all the models in the chain of routes have resolved and the transition finalized.
In the specific case shown in the update to your question, consider putting your setupController logic in an afterModel hook instead. Logically, what you are doing in setupController is not setting up the controller; it's manipulating and processing the model, and therefore rightfully belongs in one of the model hooks.
I have some code that raises a basic pubsub event:
export default Ember.Service.extend(ConnectListenersMixin, {
onGetInitial: function() {
this.trigger('listUpdated', payload);
}
This request that ends up triggering the event is made from the beforeModel hook of a route:
beforeModel: function() {
this.TodoActions.getTodos();
}
The problem is that the code that subscribes to this event is made in the init event of a component:
export default Ember.Mixin.create({
connect: Ember.on('init', function(){
// subscribing code
})
});
When the application is first loaded the event is triggered before the subscribing code has ran. After the component is initialised, then all is good.
Is there any way that I can initialize the components earlier so that they are all subscribed before the event is raised?
I'm not sure what events are available from initializers, the only one that I am aware of is registerComponentLookup, e.g.
before: 'registerComponentLookup'
Is there anyway round this?
I'm experimenting with not using proimises and not using the model hook so please don't suggest that. I know all about that.
Components are instantiated when Ember sees that they need to instantiated. If the component is instantiated from a Handlebars template, then this is naturally after the model hooks and there's nothing you can do about that. The template won't even render until the model hooks are finished, in fact.
I would take a look at doing the subscription from the controller and either have it emit events, trigger events on the components, or simply store the data so the component can bind to it. I don't have enough information about your use case to say which (or if any) is best, however.
I have an emberjs app, with a form to edit an object.
This page contains a "Save" button, which persists the data into the database.
If the object is dirty and the user is going to be transitioned to an other path in the app, I need to warn them about that, and cancel the transition if they reject the confirmation.
App.ObjectEditRoute = Ember.Route.extend
exit: ->
if #get('model.isDirty')
if confirm('All your changes will be lost')
# We just keep rolling the chain of events and do the transition
else
# We want to cancel the transition
There doesn't seem to be any way to cancel the transition from the exit method though.
Since the router facelift, this is pretty easy to do using the willTransition event.
App.ObjectEditRoute = Ember.Route.extend
events:
willTransition ->
if #get('model.isDirty')
if confirm('All your changes will be lost')
transition.perform()
else
transition.abort()
Using third party framework, I want set selected value.
Is there is any hook after view inserted into DOM in ember.js new router?
Agreed with Karl above. However, maybe you've just asked the question in a bad way. In the new router, you have the setupController, which is invoked when Ember moves into that route. So for example, if you move into /#/dashboard, then DashboardController, DashboardView, DashboardRoute will all be initialised.
Aside from the fact that you could use the didInsertElement on the DashboardView at this point, you have the setupController method which you can overwrite in the DashboardRoute. In here you can set-up the controller, and perhaps do whatever it is you're trying to do:
(The setupController will only be invoked when you enter the route, but the view won't have rendered by the time you're moving into it. For that you'll need didInsertElement and that's that. setupController is for setting up the controller, which can be thought of as an ever-persistent singleton.)
var DashboardRoute = Ember.Route.extend({
setupController: function(controller) {
// We're in the route, so let's do something now.
controller.set('myText', 'Add this to the controller!');
}
});