3

I have an array of objects(named users) which will be shown as options of dropdownlist. and I have another objects list(named selectedUsers and is saved in backend) which is used to initialize the dropdownlist.

array:

users = [
  {
    id: 2,
    name: 'name2'
  },{
    id: 2,
    name: 'name2'
  },{
    id: 3,
    name: 'name3'
  }
];

selectedUsers3 = [
  {
    id: 1,
    name: 'name1'
  },{
    id: 2,
    name: 'name2'
  }
];

I'm facing a wired situation which is when I bind Object to select options by [ngValue], and bind a function to [selected] which will check whether the current option exists in selectedUsers.

I can see the function is retrieved and the result is returned true/false as excepted, but the options keeps unselected.

template:

<select multiple [(ngModel)]="selectedUsers3">
  <option *ngFor="let user of users" [selected]="checkExist(user)" [ngValue]="user">{{user.name}}</option>
</select>

function in component:

checkExist(user) {
  return this.selectedUsers3.findIndex(selUser => selUser.id === user.id) > -1;
  //return this.selectedUsers3.filter(selUser => selUser.id === user.id).length > 0;
}

mention that I used Array.filter or Array.findIndex to check whether the data exists, and the result is correct.

Please refer this demo with the third dropdownlist, and check where am I doing something wrong? or am I missing something about [selected]? I hope someone can explain clearly about this.

UPD:

with @Günter Zöchbauer's help, this situation can be solved by using compareWith directive(refer his answer) no matter single select or multi select, but I'm still confused why they work well alongside but fail together and still trying to figure out the reason.

Pengyy
  • 37,383
  • 15
  • 83
  • 73

2 Answers2

8

selected is not supported with [(ngModel)]="selectedUser3".

To make an item selected, the value (for string only) or ngValue property value needs to match the value in selectedUser3.

this.selectedUser3 = this.users[2];

By default only object identity is checked, therefore another object instance with the same properties and values doesn't match. You can customize comparison using compareWith

https://angular.io/docs/ts/latest/api/forms/index/SelectControlValueAccessor-directive.html

<select [compareWith]="compareFn"  [(ngModel)]="selectedCountries">
    <option *ngFor="let country of countries" [ngValue]="country">
        {{country.name}}
    </option>
</select>
compareFn(c1: Country, c2: Country): boolean {
    return c1 && c2 ? c1.id === c2.id : c1 === c2;
}
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 1
    sorry to say @gunter but it is still confusing its internal structure, would be better if you provide more detail about this – Pardeep Jain May 18 '17 at 10:39
  • yes, this is how I'm doing with the first dropdown. but can you explain the reson for this? If we can deal it only with the object instance, after we got data from backend then we have to do filter again only to get the same instance from `users`. – Pengyy May 18 '17 at 10:40
  • The directive used for the option items doesn't read the `selected` property, it only **sets** the `selectedIndex` property. https://github.com/angular/angular/blob/5293794316cc1b0f57d5d88b3fefdf6ae29d0d97/packages/forms/src/directives/select_control_value_accessor.ts#L168 – Günter Zöchbauer May 18 '17 at 10:42
  • 1
    @Pengyy the `compareWith` allows to customize the comparison (see my updated answer) – Günter Zöchbauer May 18 '17 at 10:42
  • @GünterZöchbauer but when i debug, I can see that the function binded to `selected` is been called. – Pengyy May 18 '17 at 10:44
  • What do you mean by "`selected` being called". That might be some browser behavior that it reflects the value to `selected` when `selectedIndex` is set. – Günter Zöchbauer May 18 '17 at 10:51
  • @GünterZöchbauer I just add console.log into function `checkExist` and see outputs from it. – Pengyy May 18 '17 at 10:54
  • @GünterZöchbauer after read through the documentation, It seems `compareWith` doesn't support `multiple select` :( – Pengyy May 18 '17 at 10:58
  • I don't know about multiselect, I haven't used it yet myself. – Günter Zöchbauer May 18 '17 at 11:11
1

Answer to conflicts between [(ngModel)] and [selected]

after some research and debug, I find that while using ngModel on select, angular will run it's own SelectMultipleControlValueAccessor for select and own directive for option of select which lead selected to be ignored.

In the third example(example plunker), after ngModel is used, although function bind to [selected] will be called, but it's result is simply ignored.


Comments

  • comment1:
    The problem can be solved by binding [(ngModel)] and [ngValue] with same instance of objects.
  • comment2:
    The problem can also be solved by using compareWith directive, see Gunter' s anwser
  • comment3:
    This will happen also for single select, see SelectControlValueAccessor and directive for option.

New Info:

See also this issue for more information.

Pengyy
  • 37,383
  • 15
  • 83
  • 73