angular2 ngModel/ngValue select 选项对象 - 不同实例之间的平等

angular2 ngModel/ngValue select option object - equality across different instances

这个问题的风格在发布之前已经在不同版本的 Angular2 中被问过无数次。但是,我还没有在此处的 plunk 中找到任何会产生所需行为(缺少我想避免的解决方法)的东西:Select Object Problems

我的表单 select 通过 [(ngModel)] 到对象的双向绑定,然后 'option' 通过 *ngFor 生成类似对象列表(全部由 id 区分) ).在我的研究中,多次提到 Angular2 使用 JavaScript 对象等价(按实例),因此绑定模型中没有相同实例的对象将不会与列表匹配。因此,它不会显示为 "selected" 项 - 打破了“双向”数据绑定。

但是,我想为这些实例定义一种匹配方式。已经尝试了一些似乎在互联网上流传的解决方案,但我要么遗漏了一小部分,要么实施不正确。

我想避免的选项:

理想情况下(这似乎已经在几个有解决方案的地方尽可能地讨论过——在我的情况下解决方案不充分),可以为 ngModel 定义一个相等标准来代替对象实例相等.

即下面的最新尝试,其中 h.id == a.id 定义了属性 "selected"。我不明白的是为什么这个 "selected" 属性没有被渲染——它是否被 ngModel 以某种方式阻止了?在 HTML 中手动设置 selected='true' 似乎可以修复,但是使用 [attr.selected] 或构建 ng-reflect-[= 的任何其他变体生成67=]ed='true' 属性似乎不起作用。

<div *ngFor='let a of activePerson.hobbyList ; let i=index; trackBy:a?.id'>

    <label for='personHobbies'>Hobby:</label>
    <select id='personHobbies' class='form-control'
        name='personHobbies' [(ngModel)]='activePerson.hobbyList[i]' #name='ngModel'>

        <option *ngFor='let h of hobbyListSelect; trackBy:h?.id' 
            [ngValue]='h' 
            [attr.selected]='h.id == a.id ? true : null'
        >
        {{h.name}}
        </option>
    </select>
</div>

我尝试过的一些事情:

我已经成功实现了如下所示的渲染 HTML:

<select ...>
    <option selected='true'>Selected</option>
    <option selected='false'>Not Selected</option>
    <!-- and variants, excluding with selected=null-->
</select>

但是当对象实例不同时仍然没有 selected 值。当用户 select 一个值(ngModel 如何处理,以及什么可能还有其他处理选项)。

如有任何帮助,我们将不胜感激。目标是让 "Change" 按钮更改基础模型并相应地更新 select 框 - 我将我的尝试集中在 "hobbyList." 我在 Firefox 和 [=65= 上尝试过].谢谢!

当你从某个地方得到一个你想要选择的对象时,只需在 hobbyListSelect 数组中搜索匹配项并将找到的项分配给 activePerson.hobbyList[i]

没有以这种方式使用 ngModel 绑定的直接方法。 但是,由于您不想单独注册事件处理程序,我们可能需要更改分配对象的方式。 此外,您无需为 select 标记分配 id,因为它不符合目的。 对于提到的用例,我已经修改它以查看双向绑定。

使用模型找到组件的构造函数,并包含 printVal() 只是为了测试更改。模板不包括 ngValue 和 ngModel,仍然可以按预期工作。

constructor() {
    this.activePerson = {id: 'a1',
        hobbyList : [
           {
             id: 'h4'
           },
           {
             id : 'h2'
           }
          ]
      }
      
    this.hobbyListSelect = [
        {
         id: 'h1'
        },
        {
         id : 'h2'
        },
        {
         id: 'h3'
        },
        {
         id : 'h4'
        }
      ];
  }

printVal($event,i) {
    console.log(JSON.stringify(this.activePerson.hobbyList))
}
<div *ngFor='let a of activePerson.hobbyList ; let i=index; trackBy:a?.id'>
        <label for='personHobbies'>Hobby:</label>
        <select name='personHobbies' 
        (change)=" activePerson.hobbyList[i] = hobbyListSelect[$event.target.selectedIndex];
        printVal($event,i)">
            <option *ngFor='let h of hobbyListSelect; trackBy:h?.id' 
                [selected]='(h.id == a.id) ? "selected" : null'
            >
            {{h.id}}
            </option>
        </select>
    </div>

目前 angular 2 中没有简单的解决方案,但在 angular 4 中,自 beta 6 以来已经解决了这个问题 - 请参阅 https://github.com/angular/angular/pull/13349

根据@Klinki

Currently there is no simple solution in angular 2, but in angular 4 this is already addressed since beta 6 using compareWith - see https://github.com/angular/angular/pull/13349

为了说明建议案例 (see plunk) 的用法:

<div *ngFor='let a of activePerson.hobbyList ; let i=index;'>

        <label for='personHobbies'>Hobby:</label> 
        <select id='personHobbies' class='form-control'
          name='personHobbies' [(ngModel)]='activePerson.hobbyList[i]'
          [compareWith]='customCompareHobby'>
          <option *ngFor='let h of hobbyListSelect;' [ngValue]='h'>{{h.name}}</option>
        </select>

</div>
...
customCompareHobby(o1: Hobby, o2: Hobby) {
    return o1.id == o2.id;
}

[compareWith]解决了这个问题。只需在方法 compareObj 中更改您比较的对象的正确属性。

<select [compareWith]="compareObj"  [(ngModel)]="selectedObjects">
   <option *ngFor="let object of objects" [ngValue]="object">
      {{object.name}}
   </option>
</select>

compareObj(o1: Object, o2: Object) {
   return o1.id === o2.id;
}