无法在ng-repeat中的复选框上设置ng-model。

3

我有一排复选框,每次只能选择一个,如果您单击已选中的复选框,则其状态应该翻转。因此,如果它被选中并且您单击它,则应取消选中,反之亦然。

模型

this.models = [
  {name: 'blah', defaulted: false},
  {name: 'fdsf', defaulted: false},
  ...
]

模板

<input 
  type="checkbox"
  ng-repeat="model in vm.models"
  ng-checked="model.defaulted === true" 
  ng-change="vm.setDefault($index)"
  ng-model="model.defaulted" />

a. 这个不起作用,模型没有更新

http://embed.plnkr.co/O275a9O7y60NuimFYdYi?show=preview

this.setDefault = (j) => this.models = this.models.map((m, i) => {
  return j === i 
    ? Object.assign({}, m, {defaulted: !m.defaulted}) 
    : Object.assign({}, m, {defaulted: false}) 
}) 

b. 在这个例子中模型更新正确,但是你无法通过再次点击复选框来取消选择(因此在示例a中出现了!m.defaulted

http://embed.plnkr.co/MOBuN06R0hmcnpGu01Z7?show=preview

// this works, but now you can't uncheck a box that is checked
this.setDefault = (j) => this.models = this.models.map((m, i) => {
  return j === i 
    ? Object.assign({}, m, {defaulted: true}) 
    : Object.assign({}, m, {defaulted: false}) 
}) 

需要一种不使用单选框的解决方案

4个回答

2

我不喜欢你对一个数组元素的 index 进行操作。如果你对 models 集合应用任何过滤器,它将无法工作。相反,我建议你像下面这样将当前选择的模型传递给 setDefault 函数,并将当前模型标记为 true,其他模型将被设置为 false。 此外,没有必要每次使用 Object.assign 创建一个新对象副本。循环并修改现有的数组元素更有意义。

<input type="checkbox" 
  ng-change="vm.setDefault(model)"
  ng-model="model.defaulted" />

代码

this.setDefault = (j) => {
    this.models.forEach(i => i.defaulted = i.defaulted && j.name === i.name);
} 

演示 Plunker


我试过了,但实际上它并没有更新模型,只有视图更新了。 - Daniel Lizik
@DanielLizik 请现在检查答案,我最初回答时走错了路线。 - Pankaj Parkar
@PankajParkar 我认为主要问题在于您的代码没有解决复选框已经被选中的情况,它会保持勾选状态而不是取消勾选(这就是 OP 使用复选框而不是单选按钮的原因)。 - Alon Eitan
1
@AlonEitan 感谢您的提醒。我之前不知道这个情况,现在已经修复了。请看一下,如果没问题的话,请告诉我吧?很抱歉,我个人不太喜欢在这里使用 Object.assign 的概念。 - Pankaj Parkar
我个人认为这应该是被接受的答案,因为你的解决方案不会每次都用Array.prototype.map()覆盖models数组,这对性能更好。如果OP看到了这个,我不介意他们这样做。 - Alon Eitan

2

您所需要做的就是更改以下内容:

this.setDefault = (j) => this.models = this.models.map((m, i) => {
  return j === i 
    ? Object.assign({}, m, {defaulted: true}) 
    : Object.assign({}, m, {defaulted: false}) 
}) 

To

this.setDefault = (j) => this.models = this.models.map((m, i) => {
  return j === i 
    ? Object.assign({}, m, {defaulted: m.defaulted}) 
    : Object.assign({}, m, {defaulted: false}) 
}) 

通过将其更改为Object.assign({}, m, {defaulted: m.defaulted}),您可以切换当前选中/未选中复选框的状态。
编辑:请注意,@PankajParkar的答案更好,因为与我的答案不同,该答案不仅解决了主要问题,而且在性能方面也更好,因为它不使用Array.prototype.map()覆盖this.models数组-这使得angular在ngRepeat循环中重新渲染视图。
欢迎接受他们的答案。

1
啊,没错,因为我猜 ng-model 在方法调用前已经起了作用...发现得好。 - Daniel Lizik
@DanielLizik 我相信这就是原因:当用户更改输入时,立即计算给定表达式。与JavaScript onchange事件不同,该表达式将立即计算,而onchange事件仅在更改结束时触发(通常在用户离开表单元素或按下返回键时)。参考链接 - Alon Eitan

0

HTML

<input name="foo" ng-change="vm.setDefault(opt)" ng-model="opt.defaulted" type="checkbox" ng-repeat="opt in vm.models" />

JavaScript

this.setDefault = function(opt) {
    this.models.forEach((val) => {
        if(opt != val) {
            val.defaulted = false;
        }
    });
};

虽然收到了一些负面评价,但实际上它运行得非常好。

https://plnkr.co/edit/BhE7XF?p=preview


1
考虑完全删除 ng-checked,因为它不应该与 ng-model 一起使用。 - Pankaj Parkar

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接