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 中对象(即 id)以外的东西。 ngValue 是我想利用的一个很好的帮助
- 通过更改处理程序解决问题
- 强制实例匹配(我正在从数据服务获取对象,不想重新定义它们以匹配...这似乎是不必要的资源浪费)
理想情况下(这似乎已经在几个有解决方案的地方尽可能地讨论过——在我的情况下解决方案不充分),可以为 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>
我尝试过的一些事情:
- trackBy
- 绑定到 [selected]=、selected={{}} 和 [attr.selected](这看起来很接近)
我已经成功实现了如下所示的渲染 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;
}
这个问题的风格在发布之前已经在不同版本的 Angular2 中被问过无数次。但是,我还没有在此处的 plunk 中找到任何会产生所需行为(缺少我想避免的解决方法)的东西:Select Object Problems
我的表单 select 通过 [(ngModel)] 到对象的双向绑定,然后 'option' 通过 *ngFor 生成类似对象列表(全部由 id 区分) ).在我的研究中,多次提到 Angular2 使用 JavaScript 对象等价(按实例),因此绑定模型中没有相同实例的对象将不会与列表匹配。因此,它不会显示为 "selected" 项 - 打破了“双向”数据绑定。
但是,我想为这些实例定义一种匹配方式。已经尝试了一些似乎在互联网上流传的解决方案,但我要么遗漏了一小部分,要么实施不正确。
我想避免的选项:
- 绑定到 ngModel 中对象(即 id)以外的东西。 ngValue 是我想利用的一个很好的帮助
- 通过更改处理程序解决问题
- 强制实例匹配(我正在从数据服务获取对象,不想重新定义它们以匹配...这似乎是不必要的资源浪费)
理想情况下(这似乎已经在几个有解决方案的地方尽可能地讨论过——在我的情况下解决方案不充分),可以为 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>
我尝试过的一些事情:
- trackBy
- 绑定到 [selected]=、selected={{}} 和 [attr.selected](这看起来很接近)
我已经成功实现了如下所示的渲染 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;
}