2
votes

Just starting out with Ember, I wanted to perform some action when a user types something in a text field. I first tried this

<input type="text" {{bind-attr value=qty}} {{action "checkQty" on="keyUp"}}/>

That correctly initialized the text field's value and called the checkQty() function in my controller's actions hash, but I was not getting the updated value for qty. So it seems bind-attr is a one-way deal.

I tried using the TextField view:

{{view Ember.TextField value=qty onEvent="keyPress" action="checkQty"}}

I had to use keyPress instead of keyUp because onEvent only allows enter or keyPress. This worked, except that reading qty's value on keyPress gives me the value of the field before the user pressed the key and not afterward.

So I wound up using the input helper to specify the field:

{{input type="text" value=qty}}

And created an observer in my controller:

checkQty: function() {
  Ember.Logger.log('qty: ', this.get('qty'));
}.observes('qty').on('change')

So that works, but now I am confused about best practices for architecting my Ember app. It seems like the prescribed method is for your template to effectively call actions on your controller. My usage of an observer feels like a hack to get around my failure to get this working in a template.

After looking at the Ember source code, I was able to extend TextField to get the behavior I wanted:

MyApp.TextField = Ember.TextField.extend({
  keyUp: function(event) {
    this.sendAction('action', this.value);
  }
});

And my template then became:

{{view MyApp.TextField value=qty onEvent="keyUp" action="checkQty"}}

Is there a better way to accomplish this in Ember?

2

2 Answers

6
votes

You need to utilize the sendAction method, here is an example

# coffeescript code sample
window.App = Ember.Application.create()

# define the keyupaction callback on all TextField views
Em.TextField.reopen
  keyUp: (e) ->
    @sendAction('keyUpAction', e)

# the keyup callback specific for each controller
App.ApplicationController = Em.Controller.extend
  actions:
    foobar: ->
      console.log 'do yor validation here'

# the text field view
{{view Em.TextField valueBinding="firstName" keyUpAction="foobar"}}
1
votes

This can also be accomplished within the route of a template, or better yet, within a component using key-up="actionname".

Handling the data of the input field (and any others associated with it) within a component will utilize Ember's defaults instead of adding new logic and simply allow for handling the data returned.

//.js
import Ember from 'ember';

export default Ember.Component.extend({
  actions: {
    checkQty(q) {
      console.log(q);
    }
  }
});

//.hbs
{{input type="text" key-up="checkQty"}}

You could take it a step further if you need to edit the qty on the input within your action by binding it to a property.

// .js
import Ember from 'ember';

export default Ember.Component.extend({
  qty: 0,
  actions: {
    checkQty(q) {
      console.log(this.get(qty));
    }
  }
});

// .hbs
{{input type="text" value=qty key-up="checkQty"}}