从RxJS订阅回调更改后,复选框选中的属性不会更新元素

时间:2018-08-14 03:09:51

标签: javascript checkbox rxjs observable behaviorsubject

我发现了一个无法解释的非常特殊的行为。我正在使用RxJS根据BehaviorSubject的值更新一组复选框。我既不想在事件处理程序中更改选中的属性,也不想让浏览器处理它,所以我使用preventDefault并调用next来设置新值。然后,在我的subscribe回调中,尝试设置输入的checked属性,但是它似乎没有任何作用。

运行下面的代码段,然后尝试更改任何复选框。请注意,每次记录元素的名称和假定的checked值时,这似乎都表明一切正常,除了输入本身不会更改状态。我添加了其他样式,以使选中的项目更加可见。

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>

当然,这只是一个MCVE,我的实际代码分为多个文件和类。我当时在想,这可能与变量传递的方式有关,但是请注意,el.namename的记录值相同,这意味着el应该是和以前一样。

el.checked中将对setTimeout的分配包装起来似乎可以解决该问题,但是这样做绝不是正确的解决方案。我想知道是什么原因导致这种行为,以及如何在没有黑客的情况下使这种设置正常进行。

1 个答案:

答案 0 :(得分:1)

单击事件监听器后,

preventDefault()将恢复值。请参阅 Why does preventDefault on checkbox click event returns true for the checked attribute?

您可以使用setTimeout(()=> xxx,0)推迟在preventDefault运行之后发生的下一个事件循环中的执行。

const {
  BehaviorSubject,
  Observable,
  defer
} = rxjs;
const {
  distinctUntilChanged,
  map,
  mergeMap
} = 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 => {
    const {
      name
    } = e.target;
    sources[name].next(!settings[name]);

    e.preventDefault();

  });
  console.log(Observable)
  observers[name].pipe(map(value => {
    return setTimeout(() => {
      settings[name] = value;
      el.checked = settings[name];
      console.log(name, el.name, el.checked, value);
    }, 0)
  })).subscribe()
})

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>