EmberJS: reject an upcoming transition - ember.js

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()

Related

Replace browser confirm() with other library like bootbox

Browser native confirm dialog doesn't look good.
I have ember route wherein taking user confirmation to proceed if there are any unsaved changes.
I wanted to change the dialog with bootbox confirm dialog but that doesn't stop the execution as bootbox.confirm() method is async.
willTransition: function(transition){
// model dirty checking logic to set flag value
if(flag){
if(!confirm("Do you want to leave this page without save?")){
transition.abort();
}
}
}
Note: ES6 is not used in project
Since it doesn't block, the only way to do this is to:
Act as if the user said "No" then
If they click "Yes": programmatically restart whatever it was that was cancelled at step 1.

Ember: How to access element when action happens?

I am new to Ember and I am not sure how to do things the Ember way so I turn to you.
My problem:
I have a sidebar, I created a View for it. I have two buttons on the sidebar for the moment. I added an action for each button on it. I am not sure if I should handle it on controller or on view. I want on clicking one of these button, a new view to be inserted that would open a pop up menu and also the button that called the action to remain in a selected state.
I am not very sure how to do this. I tried to target the view with the action but I can't have access to the target element or at least I don't know how to access it (tried this.$()).
What way do you suggest to follow?
User 'actions' are handled with methods on a Controller or a Route. You should put them in an actions hash:
App.MyController = Ember.ObjectController.extend({
actions: {
doSomething: function() {
// do it here
}
}
});
Ember manipulates the DOM and inserts views automatically based on resources and routes. If you don't want to use the router, you can manually control the view hierarchy, but I'd suggest getting more familiar with Ember routing before you try manual views.
If I were you, I'd create a Component that handled the button. You will have a reference to the DOM element in the didInsertElement callback: http://emberjs.com/api/classes/Ember.Component.html#event_didInsertElement

Ember: Suggestions welcome for handling inFlight errors

I use Ember data with the REST Adapter. I want to make sure that in case of slow server responses, the application does not fail.
I have simulated this bij adding at server side a sleep method of 5 seconds before returning the JSON response.
If you have a form with a SAVE button, and you click this button while a previous save is still is progress, you receive a inFlight error and the whole Ember app freezes (only thing you can do is reload app). So, you can easily disable the save button by checking the isSaving state:
<button {{action 'save'}} {{bindAttr disabled="isSaving"}}>Save</button>
Now it also seems that when changing a form field while a previous save is still is progress, you receive a inFlight error. This would thus indicate that I also need to disable the complete form.
Uncaught Error: Attempted to handle event `willSetProperty` on
<App.Author:ember477:5203e34599808d1c6c000001> while in state
rootState.loaded.updated.inFlight. Called with {reference: [object Object], store:
<App.Store:ember541>, name: name}
Is there a known good practice to handle these cases ... I want to prevent that I need to add a lot of logic (disable buttons, set fields readonly, etc.) for these edge cases.
It may not be within the scope of what you are trying to do, but the Ember Persistence Foundation is designed to allow updating your models while a save is still in flight.
It is relatively trivial to migrate your models to EPF, but there are some changes required in the controller code, see "Migrating from Ember Data".

how can I use ember routeTo event?

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

Emberjs: Prevent changes propagation throughout a transaction

I have a scenario where I list products in a page and allow users to edit products' properties only in a modal window.
When a user clicks on the product and start editing the product's properties, changes gets propagated right away to the product in the product list page and any part of page bound directly or indirectly to the property undergoing the changes. Not the desired behaviour. Changes should be propagated only when the user confirms the changes i.e save button clicked, transaction committed and modal window closed
transaction: APP.store.transaction(),
renderTemplate:function(controller, record) {
this.transaction.add(record);
// append the modal view
App.ModalView.create().append();
},
onSaveClick: function(record) {
this.transaction.commit();
this.transitionTo('products');
},
Ideally i need to instruct Ember to stop propagating any changes on the product until the transaction is committed. I tried to use beginPropertyChanges and endPropertyChanges to defer notifying any related observers this way:
renderTemplate:function(controller, record) {
this.transaction.add(record);
record.beginPropertyChanges();
// append the modal view
App.ModelView().create().append();
},
onSaveClick: function(record) {
record.endPropertyChanges();
this.transaction.commit();
this.transitionTo('products');
},
And this prevents the changes from propagating to the list, however the transaction is not committed and the item is still dirty when the modal window is closed.
I'm quite sure i'm not using this properly. I need a way to keep changes away from the store as long as the item is uncommitted as reflected by its currentState
Using the item status isDirty doesn't help to solve this problem because when it's used the item is completely removed from the list and that's not acceptable.
{{#each item in controller}}
{{#unless item.isDirty}}
{{item.name}}
{{/unless}}
It is the exact same object you are editing, because the textfield, you are typing, in is bound to the property and the representation in the main list is also bound to the same property they both get updated.
Ember-data takes care op persisting object to the back end not between states in your application.
You should have a look at this. It explains how to make a lazy textfield object one that doesn't update the object immediately with a binding but only after save button is clicked.