focus() on Select field fired before Ember updates disabledBinding attribute - ember.js

In my application I have an extended Em.Select view:
App.SelectField = Em.Select.extend({
attributeBindings: ['disabled'],
actions: {
setFocus: function(){
this.$().focus();
}
}
});
Which is used in my handlebar template:
{{view App.SelectField
viewName="cbAddressType"
disabledBinding='getIsDisabled'
contentBinding="controllers.addressTypes"
optionValuePath="content.id"
optionLabelPath="content.addressTypeName"
valueBinding="getSetAddressType"}}
The view has a disabledBinding bound to the view's controller property getIsDisabled. When this property changes, the Select view is either enabled or disabled. This works just fine.
However, I also have an action on the template:
<button {{action "editRecord" view.cbAddressType}}>Edit</button>
This calls the controller's editRecord action:
actions: {
editRecord: function (fieldToFocusOn) {
this.toggleProperty('isEditing');
if (fieldToFocusOn && this.get('isEditing'))
{
fieldToFocusOn.send("setFocus");
}
}
}
Which sends the setFocus action to the above SelectField, which in turn runs the focus() method on the select control.
The problem is that focus() is fired BEFORE the disabledBinding attribute on the Select is updated with the bound property's changed value in the controller. The control cannot receive the focus until it is enabled. I tried a while select.disabled [wait] loop but that just led to an infinite loop as it appears that the view is not refreshed until after the setFocus is called - they are not run asynchronously.
What I also note is that on a Ember.TextField with a readonlyBinding instead of a disabledBinding that this methodology works fine: the text field's readonly attribute is removed before the focus() event is fired.
Grateful for any advice.

Not tested but could work. In your App.SelectField use:
setFocus: function(){
Ember.run.schedule('afterRender', this, function() {
this.$().focus();
});
}

Related

focus-out event in Ember.Select view

I have used Ember.Select view as follows.
{{view "select" content=people
optionLabelPath="content.fullName"
optionValuePath="content.id"
prompt="Pick a person:"
selection=selectedPerson}}
Now I want to add focus-out="showErrors" event listener to this select view in order to handle some validation. This works perfectly with Ember.TextField and Ember.TextArea. But I noticed the focus-out does not work with Ember.Select view.
It would be really nice if someone can provide a solution to this problem
Could you add an observer to selectedPerson so that when the user changes the dropdown value this will trigger your validation.
Older syntax:
validatePersonChange: function(){
//do validation here
}.observes('selectedPerson')
Newer syntax:
const { observer } = Ember;
...
validatePersonChange: observer('selectedPerson', function(){
//do validation
})

ember select change model property only on button click

I have an ember Select that is bound to a property of the model in a modal dialog.
I want the model property to change only if the user clicks OK (and gets reverted if he clicks cancel).
How can I do that?
edit-session-modal.hbs:
{{#modal-dialog title='Change status (select empty to return to original status)' ok='save' close='closeModal'}}
<form {{action 'ok' on='submit' target=view}}>
<div class="form-group">
<label>Status</label>
{{view "select" content=sessionStatuses selection=model.status}}
</div>
</form>
{{/modal-dialog}}
controllers/edit-session-modal.js:
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
close: function() {
return this.send('closeModal');
}
},
sessionStatuses: ['', 'SUCCESS', 'FAILURE', 'RUNNING'],
selectedStatus: "model.status"
});
You can achieve it by not binding the model.status property to selection property of Ember.Select, but use some kind of a buffer.
Let's say that your template look like this:
{{view "select" content=sessionStatuses selection=userSelection}}
And your controller should have following actions:
actions: {
close: function() {
this.set("selection", undefined); # or any other that should be default
# everything else, like closing the modal
},
save: function() {
this.set("model.status", this.get("selection"));
# everything else, like saving the model
}
}
EDIT after #Boaz reference
In #Boaz example there is no route defined for that controller. The solution turned out to be creating view object with didInsertElement as follows:
didInsertElement: function() {
modelStatus = this.get('controller').get('model.editedStatus');
this.get('controller').set('selectedStatus', modelStatus);
}

Can I access the HTML el (button) from an Ember {{action}}?

I have the need to act on the HTML element which triggers an Ember action.
The scenario is simple, let's say I have multiple rows of items, each having an action that pulls some data via an ajax. I want to disable that particular element until the ajax request has completed.
I checked this property but it refers to the Component Class.
So in the below markup, I'd like to capture the element in my component class.
<td><button {{action 'cancel' transaction}} class="btn-danger btn">Cancel</button></td>
There is not a default way to disable actions in Ember.
You could do it in several ways:
1) With CSS and bound controller properties.
a.is-inactive {
pointer-events: none;
cursor: default;
}
Then you must bind the className to the controller property (Inactive).
<a {{action 'cancel'}} {{bind-attr class="isInactive:is-inactive"}}
2) You could check in your route action that your actions is disabled. Use any specific controller to save the application state.
cancel: function() {
var controller = this.controller;
if ( controller.get('pendingAction') ) return;
controller.set('pendingAction', true);
this.ajax(...., function() {
....
controller.set('pendingAction', false);
}, function() {
....
controller.set('pendingAction', false);
});
}
3) Built your own way..
I have discussed this case at the discourse forum.

Evaluating controller property everytime the template is rendered in Ember

I have a template called sample and I am changing the property show in the controller based on a button press. Now I want the controller to reset the property every time the template is rendered. Currently even if I go to 'next' template and come back, the property show remains true if the button on sample was pressed. I want to change this behaviour and the property show should be false by default. I know I can do this by defining a view and using the didInsertElement hook but is that the only way to do this?? Ember.js website says that Views in Ember.js are typically only created for the following reasons:
When you need sophisticated handling of user events
When you want to create a re-usable component
and I am doing none of the above. Here is some sample code:
<script type="text/x-handlebars" data-template-name="sample">
{{#if show}}
Showing stuff
{{/if}}
<button {{action changeShow}}>Change</button>
{{#link-to 'next'}} Next {{/link-to}}
</script>
<script type="text/x-handlebars" data-template-name="next">
Hello
{{#link-to 'sample'}}Back{{/link-to}}
</script>
App.SampleController=Ember.Controllers.Extend{(
show:false,
actions:{
changeShow:function(){
this.controllerFor('sample').set('show',true);
}
}
)};
you can use didTransition action which will trigger automatically once the transition happened. didTransition action
App.SampleController=Ember.Controllers.Extend{(
show:false,
actions:{
didTransition:function(){
this.controllerFor('sample').set('show',false);
},
changeShow:function(){
this.controllerFor('sample').set('show',true);
}
}
)};
You can use the renderTemplate hook for the route you're doing this in, and change the controller variable in there.
http://emberjs.com/api/classes/Ember.Route.html#method_renderTemplate
I'd do something like this:
App.PostsRoute = Ember.Route.extend({
renderTemplate: function(controller, model) {
var favController = this.controllerFor('favoritePost');
favController.set("toggle", false)
this._super()
}
});

Generating forms and handling submit properly with Ember.js rc1

I'm having trouble figuring out how to properly populate and accept an update from an Ember form under RC1. I've boiled it down to the bare essentials in this jsfiddle. I've made it far enough to display the form for a particular entity (user with first and last name) and the current values populate in the fields. However, as the user types, the fields actually update with each keystroke, and clicking the back button reveals that the data has already been changed without clicking the update button. I'd prefer to keep some logic in between the updates and only confirm an update after the user clicks the update button.
{{#view App.PersonFormView}}
First name: {{view Ember.TextField valueBinding="firstName"}}
Last name: {{view Ember.TextField valueBinding="lastName"}}
<button {{action "updatePerson"}}>Update</button>
{{/view}}
In the form template, I was trying to follow one of the Ember.js examples, but doing so resulted in a long delay and a monstrous deprecation warning using RC1. I think the examples are still being updated. I'd prefer a more handlebars-elegant way of coding the form if it existed.
The second problem is that I cannot capture the submit event itself, either on the form view or the controller. I don't know where this event is going.
App.PersonFormController = Ember.ObjectController.extend({
updatePerson: function(params){
// this doesn't get triggered as I would have expected
console.log('controller updatePerson: '+params);
}
});
App.PersonFormView = Ember.View.extend({
tagName: 'form',
updatePerson: function(params){
// this doesn't get triggered either!
console.log('updatePerson params: '+params);
}
});
In summary, I need to:
populate the input fields with the values without having them linked directly back to the model's data while the user is typing
catch the submit button's (or other control would be fine) clicked event along with the fields - and the entity's id - so that I can set them back on the model's data manually
There are several things:
I cannot capture the submit event itself
Events are fired in the controller and the route, not the view. The reason why your controller PersonFormController wasn't catching the event, is because the name is wrong. The controller should be named after the route: EditPersonController.
It's generally good to pass the model along with the action:
<button {{action "updatePerson" content}}>Update</button>
Here is an updated version that catches the event: http://jsfiddle.net/teddyzeenny/L9HMm/5/
populate the input fields with the values without having them linked directly back to the model's data
It's generally good practice to bind the fields directly to the model, to avoid code duplication.
Your problem is not that the fields are bound directly to the model, it's that you have no control over what is happening (saved, not saved, left the route...)
To have solid control, it's best to put your updating logic in your route. That way you can act accordingly when the user enters/leaves the route.
To catch your events in the route:
App.EditPersonRoute = Ember.Route.extend({
events: {
updatePerson: function(record) {
record.one('didUpdate', this, function() {
this.transitionTo('index');
});
record.get('transaction').commit();
}
}
});
To rollback changes if the user doesn't click on Update, use the deactivate callback in the route:
App.EditPersonRoute = Ember.Route.extend({
deactivate: function() {
this.modelFor('editPerson').get('transaction').rollback();
},
events: {
updatePerson: function(record) {
record.one('didUpdate', this, function() {
this.transitionTo('index');
});
record.get('transaction').commit();
}
}
});
Now these won't work in the fiddle since you are not using ember-data models.