Emberjs: Prevent changes propagation throughout a transaction - ember.js

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.

Related

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

Observer fires too early

I have three drop-downs in my view. While preparing a controller, I am doing the following:
Pre-set the bound properties (selectedCountry, selectedSubtype, selectedCity) (this.set('selectedCountry', 'DE');)
After the properties are set, I add observers for those properties, so that the drop-downs can be re-configured whenever the user is changing the selection. I am careful to add the observers after I have initialized the properties, so that I am not firing the controllers accidentally.
But, as soon as the view is rendered, the observers are immediately fired. The user has performed no action yet. I do not understand this, but I imagine that, when rendering the view in the DOM, ember sets the bound property in the drop-downs, and this causes the observers to fire. That is:
Ember renders the view
Sees the binding in the template, to the controller property
Sets the DOM element property according to the controller property
And this somehow causes the firing of the observer?!? why!?!?
If this is the case, the only solution that I can think of, would be to configure the observers after the view has been rendered. There is a didInsertElement for Views, but my problem is controller-related. So, I am confused: is the job of the controller to care about rendering issues? I do not think so: that should be the view's responsibility! But the controller is clearly affected by the rendering order! How to solve this in a clean way?

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".

Ember.js - how do I bind a view to a state manager, and ensure that view is rendered?

I am using a StateManager to control the state of a pop-up modal (e.g., the states are "open.edit", "open.show" and "closed"). I'd like to use a state manager here because the modal is quiet complex and requires it's own transaction (I'm using Ember data).
I am able to set the appropriate data, view and controller on my ModalStateManager.
However, the view (in this case App.ModalView) is never rendered in the DOM. I know this because I've put logging statements in didInsertElement function of my App.ModalView, and those never get logged.
How can I render the view when someone clicks the button to open the modal?
Here's the code that is run on my ModalStateManager when someone clicks to open the modal.
App.ModalStateManager = Ember.State.create({
closed: Ember.State.create({
open: function(manager, modalData) {
var view = App.router.get('applicationController').connectOutlet("modal", modalData);
//this is working
//the view returned is the ModalView; it has a ModalController with expected content
manager.transitionTo('open.show');
}
})
//omitting other states for simplicity
)}
Larger question: How should you build a view that has multiple states and dynamic data, but does not have its own url or state within the router? e.g., Imagine a page with a list of unique items. Clicking an item pops open a modal that shows the item content, allowing the user to edit and save it. The modal doesn't have its own url or state in the router, so its not as easy as setting a dynamic state /:item_id in the router that can be easily wired and updated.
In one of our apps, we have a PanelManager (subclass of StateManager) that handles state for our modals. There is also a PanelController, which has properties that our panel container view binds to for className and visibility. When transitioning from closed to a particular open state (e.g. showingEditPanel), the manager sets the classname and visibility properties and calls connectOutlet on the panelController to show the correct view/controller combo within the panel container. Additional complexity can be modeled with nested states under each open state.

Ember.js textField change event

I just start to learn ember.js
Why this code http://jsfiddle.net/alexchetv/RVNzP/ don't work properly -- App.MyTextField.change() execution is triggered only after MyTextField loses focus?
Alternative code with the same functionality works as expected.
Keep in mind that handlers on your Ember.Views (change, select, click, etc) are bound to normal HTML DOM events. "onchange" only gets called on an input field when the field loses focus and has changed, not any time the value is modified. You should observe 'value' if you want to be notified of changes to the displayed value.
Here's a working solution.
What I've done is made formDirty a computed value, which recomputes upon changes in the input. Ember, unlike the native "change" event, updates the the Input's value on each keystroke (copy/paste event etc).
Ember's binding makes sure the peopleController is always updated with the input value, thus also updating the computed field formDirty.
Note that if you add more inputs, you must tell the computed property to listen to its events, e.g.
formDirty: function() {
return !Ember.empty(this.get('fName')) && !Ember.empty(this.get('lName'));;
}.property('lName', 'fName').cacheable() // <- dependencies
cacheable() is used for performance sake only, meaning don't computed again until the dependencies change.