1

I have a collection of select boxes and input fields in my Aurelia project and I need to disable one specific select box everytime a value changes on a specific input field. The problem is that everytime I change the value on the input field the disabled state of the select box doesn't change.

I use the BindingEngine to observe everytime an input field value changes. I have an array of booleans that represents the binding values that are bound to the disable.bind attribute of each select box.

I would like to know how to change the disable.bind state of a specific select box everytime the value changes in the input.

Expected behavior: if the input field value is greater than 0 i enable the select else i disable it.

I have a demo here: https://gist.run/?id=726eef81bb88e8a695d4ae5df6b2e679

Arton Hoxha
  • 343
  • 3
  • 12
  • Could you elaborate a little bit on your expected behavior? From what I can tell in the code, if the value in the `input` is set to 0, it should get disabled, and if it's set to anything higher than 0, it should not be disabled. Am I correct? – Jesse May 09 '18 at 08:53
  • Yes that's exactly what i want but as you can see, the disable state doesn't change even if the value in the array changes – Arton Hoxha May 09 '18 at 09:01
  • There appears to be a problem with binding to the boolean array. I changed the `array` to a single boolean `this.disabled` and that seemed to work fine. I think the binding doesn't update because it doesn't listen to the mutation of the array. That's as far as I've gotten, hopefully some other members can help you with a solution. – Jesse May 09 '18 at 09:20

1 Answers1

3

The problem is very subtle. This is your array:

this.array = [true,false,true,false];

And you're binding to it like so:

<select disabled.bind="array[$index]">

You think you're binding to a variable (namely, the array) but what you're really binding to is the value in that index of the array which is the literal value true or false.

For index 0, the binding is effectively the same as this:

<select disabled.bind="true">

Literal values, of course, cannot change - only the variables that hold them can.


If you change your array to something like this:

this.array = [{disabled:true},{disabled:false},{disabled:true},{disabled:false}];

And your binding to this:

<select disabled.bind="array[$index].disabled">

Then it should work.

Note:

There are several improvements I'd make in your code overall, not the least of which is to dispose your subscriptions on the opposite lifecycle. See this updated gist: https://gist.run/?id=a01425ec61a4dc7c1997408ff92c52a2

Community
  • 1
  • 1
Fred Kleuver
  • 7,797
  • 2
  • 27
  • 38
  • Very subtle indeed, didn't notice it myself. If you plan on doing this, I'd probably make an array combining the two instead. Something like `this.array = [{value: 0, disabled: true},{value: 1, disabled: false}]` etc. Makes it easier in terms of structure. You can also repeat over this array and bind more easily that way. ` – Jesse May 09 '18 at 12:14
  • Indeed, there are several improvements possible. I included an updated gist in my answer to reflect this. – Fred Kleuver May 09 '18 at 12:25
  • Can't you do a `forEach` in the `unbind()` to dispose all the subscriptions? Or is it recommended to pop the subscriptions off the array? – Jesse May 09 '18 at 12:34
  • Thanks for your answers of course it would be better to do like this. – Arton Hoxha May 09 '18 at 12:43
  • @JessedeBruijne Simply disposing the subscriptions would leave disposed subscriptions in the array which will throw if you try to dispose them again later, so you want to clear the array as well. You could `forEach` and then assign a new array, but this particular method of doing it just happens to be the most performant one and I personally think it's nice and clean - though that's just a matter of taste. – Fred Kleuver May 09 '18 at 12:56
  • Fred, I want to mention that the reason the binding never gets updated is not because the the value at `array[$index]` is a boolean. The reason the binding never gets updated is that Aurelia is watching for changes to either `array` or `$index`. When the value at that spot in the array changes, Aurelia doesn't notice since it isn't watching the actual value. As I think about it, I think this might be worth filing an issue to discuss this. – Ashley Grant May 09 '18 at 15:22
  • Ashley, in my answer, the binding has 3 observers (`array`, `$index` and `disabled`) whereas in OP's example it only has 2 (`array` and `$index`). This is the correct behavior - the parser stops at a literal primitive and returns the value rather than an observable expression. No observer is created as there is nothing to observe. Consider how you would watch for changes to a literal value in an array - there is no object to intercept a getter or setter on, and the array or index doesn't change, so you'd have to dirty check every value in the array. It's really not *supposed* to work. – Fred Kleuver May 10 '18 at 02:16
  • So you're correct in that the binding doesn't get updated because Aurelia is watching for changes to either `array` or `$index`. It's only watching for those because the value is a boolean which can't be watched. – Fred Kleuver May 10 '18 at 02:22