0

This is a simplified example but demonstrates my problem well. I have an input for a PIN with buttons for digits (1, 2, 3, 4). The action on my buttons should set a property pinValue which is passed into the wrapped component pin-input. So this is what my code looks like:

See example: http://ember-twiddle.com/38bd66b63e6745f2ea0d

pin-entry.hbs

{{pin-input pinValue=pinValue}}
{{#each numbers as |num|}}
    <button {{action "addNumber" num}}>{{num}}</button>
{{/each}}

pin-entry.js

pinValue: null,
numbers: [1, 2, 3, 4],
actions: {
    addNumber: function (number) {
        this.set('pinValue', number);
        this.notifyPropertyChange('pinValue');
    }
}

pin-input.hbs

<input type=text value={{value}}>

pin-input.js

value: null,
pinValue: null,

onInsertAddObserver: function() {
    // trying to manually subscribe to pinValue here.
    // * from stackOverflow answer
    var controller = this.get('targetObject');
    controller.addObserver('pinValue', this, this.onDataChange)
}.on('didInsertElement'),

onDidChangeInput: function() {
    var current = this.$('input').val();
    this.set('value', current + this.get('pinValue'));
}.observes('pinValue')

The problem is when trying to input duplicate numbers back to back (e.g. 1233 will stop at 123). I cannot seem to force a property change so that onDidChangeInput will fire. The addObserver method is from another post*.

*Some ideas taken from here but did not help me: EmberJS notifyPropertyChange not making it to observer?

Community
  • 1
  • 1
Jeff
  • 2,293
  • 4
  • 26
  • 43

2 Answers2

1

Since data down and actions up

I would go with something like this.

{{pin-input pinCode=current}} // Explaining it later 
{{#each numbers as |num|}}
    {{pin-number nr=num numberChanged="numberChanged"}}
{{/each}}

pin-number.js

actions: {
    addNumber: function (number) {
        this.sendAction('numberChanged', number); // To the controller
    }
}

controller.js

   actions: {

     numberChanged: function(newNr) {
       var current = this.get('current');

       if(Ember.isNone(current) || current.length >= 4) { 
          this.set('current', newNr);
       } else {
          this.set('current', this.get('current') + "" + newNr);
       }

   }

And now we bind current to pin-input

{{pin-input pinCode=current}}
kristjan reinhold
  • 2,038
  • 1
  • 17
  • 34
1

I ended up solving it in with a simple, albeit inelegant, solution. For some reason, even if you pass a property into another component, changes in the main component's property don't cause observers in the wrapped component to fire if the value hasn't changed. Even if you call notifyPropertyChange() they still won't fire. The really simple solution I came up with is:

See the twiddle here (just the important parts below)

pin-entry.js

pinValue: null,
_cachedValue: null,

actions: {
    addNumber: function (number) {
        this.set('pinValue', number);
        // just cache the value and if you detect a duplicate
        // then force the property to update
        if (this.get('_cachedValue') === number) {
            this.notifyPropertyChange('pinValue');
        }
        this.set('_cachedValue', number);
    }
}

pin-input.js

// this is how you have to subscribe.
onInsertAddObserver: function() {
    var c = this.get('targetObject');
    c.addObserver('pinValue', this, this._onChange)
}.on('didInsertElement'),

Anyone think this is way off?

Jeff
  • 2,293
  • 4
  • 26
  • 43
  • Observers are bad practise in general, i provided u a decent solution but what the heck. Just to clarify why: https://www.youtube.com/watch?v=vvZEddrClAQ – kristjan reinhold Oct 30 '15 at 14:02
  • yeah, I use them sparingly, but they're not as evil as everyone says. I tried your example and it didn't work. If you plug your code into twiddle so I can see it working, then I'll mark your answer as correct. – Jeff Oct 30 '15 at 14:16