53

I've managed to get a Select list to bind with my model for the purpose of saving, but I cannot work out how to make Angular2 automatically select the correct option on the Select list if I'm providing editing functionality. In other words, if I'm editing a pre-existing object via a form, I need the Select list to reflect the initial state of the object (e.g. option 5 in the select list), rather than it just defaulting to the first item.

<select [ngModel]="originalObject">
    <option *ngFor="let object of objects" [ngValue]="object">{{object.name}}</option>
</select>

How I imagine it should work, but doesn't!

<select [ngModel]="originalObject">
    <option *ngFor="let object of objects" [ngValue]="object" [selected]="object === originalObject">{{object.name}}</option>
</select>

So essentially I'm trying to make use of the 'selected' property on option, but for whatever reason it doesn't do anything. The 'selectedObject' in this case would be an object in the component that it can read.

user3452805
  • 1,511
  • 2
  • 11
  • 14
  • 1
    The `selected` property is overridden by `originalObject`. `ngValue` only works with `ngModel`. Without `ngValue` you can only use string values, not objects with ` – Günter Zöchbauer Jun 06 '16 at 18:41
  • At present I'm not using the `[selected]=..`, that was just the way I thought it should work. It's not there at the moment but it doesn't select the correct option on load. – user3452805 Jun 06 '16 at 19:19

6 Answers6

62

Okay, so I figured out what the problem was, and the approach I believe works best. In my case, because the two objects weren't identical from a Javascript perspective, as in: they may have shared the same values, but they were different actual objects, e.g. originalObject was instantiated entirely separately from objects which was essentially an array of reference data (to populate the dropdown).

I found that the approach that worked best for me was to compare a unique property of the objects, rather than directly compare the two entire objects. This comparison is done in the bound property selected:

<select [ngModel]="originalObject">
    <option *ngFor="let object of objects" [ngValue]="object" [selected]="object.uniqueId === originalObject.uniqueId">{{object.name}}</option>
</select>
user3452805
  • 1,511
  • 2
  • 11
  • 14
  • 3
    This works! , you have to remove [ngModel]="originalObject" from the select statement : – Mayank Raipure Nov 26 '17 at 02:12
  • Important aspect is that two distinct instances with the exact same properties will not match. i.e. retrieved by different api calls (which was the case for me). Then just replace the prop used as value with the one from the optionsas follows: myObj = this.options.filter(o => o.id === myObj.id)[0]; Just note that then modifying the object will update it in the options collection too. – Harps Jan 31 '18 at 09:35
56

Update to angular 4.X.X, there is a new way to mark an option selected:

<select [compareWith]="byId" [(ngModel)]="selectedItem">
  <option *ngFor="let item of items" [ngValue]="item">{{item.name}}
  </option>
</select>

byId(item1: ItemModel, item2: ItemModel) {
  return item1.id === item2.id;
}

Some tutorial here

Pipo
  • 4,653
  • 38
  • 47
mrebval
  • 726
  • 7
  • 10
9

If you use

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

You need to set the property object in you components class to the item from objects that you want to have pre-selected.

class MyComponent {
  object;
  objects = [{name: 'a'}, {name: 'b'}, {name: 'c'}];
  constructor() {
    this.object = this.objects[1];
  }
}
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Apologies, I think I accidentally called two objects `object` in the question. I've amended the question now. The `originalObject` and `objects` both exist and contain data. `originalObject` is essentially what the selected list item should be, and `objects` is the array of possible options, one of which would be `originalObject`. – user3452805 Jun 06 '16 at 18:36
  • [**Plunker example**](https://plnkr.co/edit/a9vlFiB6GuoIbmksWA8c?p=preview) exactly like in my answer. – Günter Zöchbauer Jun 06 '16 at 19:23
  • Ah I see. It seems as though it could be to do with it being a reference? If you change the code in the Plunker to `this.originalObject = {name: 'b'};`, the select list will no longer default to `b`, rather it defaults to the first element in the array, `a`. Any ideas on this? – user3452805 Jun 06 '16 at 20:26
  • 1
    Thanks for your responses, as they led me to the answer. I believe I now understand that the root of my problem is the way Javascript compares objects. I can't really compare two objects that were instantiated entirely separately. – user3452805 Jun 06 '16 at 21:11
  • @GünterZöchbauer this is exactly the right answer but sometimes select doesnt show the selected option (even though it is selected correctly), any idea? After I opening and closing the select, it remembers to select the option. Can it be related to size of the object (I have huge object bound to option) or maybe it is being late for selecting the item? – omeralper Aug 23 '16 at 09:25
  • Hard to tell. Can you provide a plunker that allows to reproduce the issue? – Günter Zöchbauer Aug 23 '16 at 09:27
  • @GünterZöchbauer i tried this still no result coming http://stackoverflow.com/q/43311162/4773561 – Shreekant N Apr 09 '17 at 19:52
3

You can achieve the same using

<select [ngModel]="object">
  <option *ngFor="let object of objects;let i= index;" [value]="object.value" selected="i==0">{{object.name}}</option>
</select>
Ikhlak S.
  • 8,578
  • 10
  • 57
  • 77
Jinu
  • 93
  • 1
  • 6
0

I hope it will help someone ! (works on Angular 6)

I had to add lots of select/options dynamically and following worked for me:

<div *ngFor="let option of model.q_options; let ind=index;">

        <select 
          [(ngModel)]="model.q_options[ind].type" 
          [ngModelOptions]="{standalone: true}"
        > 
          <option 
            *ngFor="let object of objects" 
            [ngValue]="object.type" 
            [selected]="object.type === model.q_options[ind].type"
          >{{object.name}}
          </option>
        </select> 

        <div [ngSwitch]="model.q_options[ind].type">
          ( here <div *ngSwitchCase="'text' or 'imagelocal' or etc."> is used to add specific input forms )
        </div>
</div>

and in *.ts

// initial state of the model
// q_options in html = NewOption and its second argument is option type
model = new NewQuestion(1, null, 2, 
  [
    new NewOption(0, 'text', '', 1), 
    new NewOption(1, 'imagelocal', '', 1)
  ]);

// dropdown options
objects = [
    {type: 'text', name: 'text'},
    {type: 'imagelocal', name: 'image - local file'},
    {type: 'imageurl', name: 'image URL'}
   ( and etc.)
];

When user adds one more 'input option' (pls do not confuse 'input option' with select/options - select/options are static here) specific select/option, selected by the user earlier, is preserved on each/all dynamically added 'input option's select/options.

Ula
  • 2,628
  • 2
  • 24
  • 33
0

The easiest way to solve this problem in Angular is to do:

In Template:

<select [ngModel]="selectedObjectIndex">
  <option [value]="i" *ngFor="let object of objects; let i = index;">{{object.name}}</option>
</select>

In your class:

this.selectedObjectIndex = 1/0/your number wich item should be selected
Samuel Knoch
  • 1,113
  • 8
  • 9