lundi 13 août 2018

Checkbox checked property does not update element when changed from RxJS subscribe callback

I found a very peculiar behavior that I cannot explain. I'm using RxJS to update a set of checkboxes based on a BehaviorSubject's value. I don't want to change the checked attribute in the event handler nor let the browser handle it, so I use preventDefault and call next to set the new value. Then, in my subscribe callback I attempt to set the input's checked attribute, but it seemingly has no effect.

Run the snippet below, and try changing any of the checkboxes. Notice that each time the element's name and supposed checked value is logged, which would seem to indicate that everything's in order, except the inputs themselves do not change states. I added additional styling to make checked items more visible.

const { BehaviorSubject } = rxjs;
const { distinctUntilChanged } = rxjs.operators;
const $checkboxes = Array.from(document.querySelectorAll('input[type="checkbox"]'));
const settings = {
  a: false,
  b: true,
  c: false,
  d: false,
  e: true,
  f: true,
}
const sources = {};
const observers = {};
$checkboxes.forEach(el => {
  const { name } = el;
  el.checked = settings[name];
  sources[name] = new BehaviorSubject(el.checked);
  observers[name] = sources[name].asObservable().pipe(distinctUntilChanged());
  el.addEventListener('click', e => {
    e.preventDefault();
    const { name } = e.target;
    sources[name].next(!settings[name]);
  });
  observers[name].subscribe(value => {
    settings[name] = value;
    el.checked = settings[name];
    console.log(name, el.name, el.checked, value);
  });
})

console.clear();
label {
  display: inline-block;
  margin: 3px;
}

input:checked + span {
  font-weight: bold;
  text-decoration: underline;
}
<label><input type="checkbox" name="a"> <span>Option A</span></label>
<label><input type="checkbox" name="b"> <span>Option B</span></label>
<label><input type="checkbox" name="c"> <span>Option C</span></label>
<label><input type="checkbox" name="d"> <span>Option D</span></label>
<label><input type="checkbox" name="e"> <span>Option E</span></label>
<label><input type="checkbox" name="f"> <span>Option F</span></label>

<script src="https://unpkg.com/rxjs@6.2.2/bundles/rxjs.umd.min.js"></script>

Of course, this is just an MCVE, my actual code is split between multiple files and classes. I was thinking that maybe it has something to do with the way the variables are passed around, but notice that el.name and name are identical in the logged values, meaning that el should be the same element as before.

Wrapping the assignment to el.checked in a setTimeout appears to solve the issue, but doing that is almost never the right solution. I'd like to know what's causing this behavior and how I could make this kind of setup work without hacks.




Aucun commentaire:

Enregistrer un commentaire