【问题标题】:Checkbox checked property does not update element when changed from RxJS subscribe callback从 RxJS 订阅回调更改时,复选框选中的属性不会更新元素
【发布时间】:2018-08-14 03:09:50
【问题描述】:

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

运行下面的 sn-p,并尝试更改任何复选框。请注意,每次记录元素的名称和假定的 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 中似乎可以解决问题,但这样做几乎不是正确的解决方案。我想知道是什么导致了这种行为,以及如何在没有黑客攻击的情况下使这种设置工作。

【问题讨论】:

    标签: javascript checkbox rxjs observable behaviorsubject


    【解决方案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>

    【讨论】:

    • 感谢您提供解释链接,我不知道 preventDefault 导致了这种情况,但现在它确实有意义。看起来 setTimeout 毕竟解决这个问题的最简单方法。
    猜你喜欢
    • 2018-05-06
    • 1970-01-01
    • 2014-06-16
    • 2012-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-27
    • 1970-01-01
    相关资源
    最近更新 更多