Skip to content

How to use select/option/NgFor on an array of objects? #4843

@LMFinney

Description

@LMFinney

I'm having trouble creating a select in Angular2 that is backed by an array of Objects instead of strings. I knew how to do it in AngularJS using ngOptions, but it doesn't seem to work in Angular2 (I'm using alpha 42).

In the sample below, I have five selects, but only three of them work.

  1. 'Select String' is a simple string-based select, and it works fine.
  2. 'Select Object via 2-way binding' was my attempt to use 2-way binding. Unfortunately, it fails in two ways - when the page loads, the select shows the wrong value (foo instead of bar), and when I select an option in the list, the value '[object Object]' gets sent to the backing store instead of the correct value.
  3. 'Select Object via event' was my attempt to get the selected value from $event. It fails in two ways, too - the initial load is incorrect in the same way as (2), and when I selection an option in the list, the value '[object Object]' is retrieved from the event, so I can't get the right value. The select gets cleared.
  4. 'Select Object via string' is an approach that uses an object that works. Unfortunately, it really works by using the string array from (1) and converting the value from string to object and back.
  5. 'Select Object via JSON' might be a bit better than (4) because it doesn't require the string array, but it does involve using JSON each way. Also, I'm not sure if getting the Event from (change) is better or worse than getting the stringified objected from (ng-model-change). (@Chocoloper helped me with this version)
import {Component, FORM_DIRECTIVES, NgFor, Inject} from 'angular2/angular2';
import {RouteParams} from 'angular2/router';

interface TestObject {
  name:string;
  value:number;
}

@Component({
  selector: 'app',
  template: `<h4>Select String</h4>
            <select [(ng-model)]="strValue">
                <option *ng-for="#o of strArray" [value]="o">{{o}}</option>
            </select>

            <h4>Select Object via 2-way binding</h4>
            <select [(ng-model)]="objValue1">
                <option *ng-for="#o of objArray" [value]="o">{{o.name}}</option>
            </select>

            <h4>Select Object via event</h4>
            <select [ng-model]="objValue2" (change)="updateObjValue2($event)">
                <option *ng-for="#o of objArray" [value]="o">{{o.name}}</option>
            </select>

            <h4>Select Object via string</h4>
            <select [ng-model]="objValue3.name" (change)="updateObjValue3($event)">
                <option *ng-for="#o of strArray" [value]="o">{{o}}</option>
            </select>

            <h4>Select Object via JSON</h4>
            <select [ng-model]="stringify(objValue4)" (ng-model-change)="updateObjValue4($event)">
                <option *ng-for="#o of objArray" [value]="stringify(o)">{{o.name}}</option>
            </select>

            <div><button (click)="printValues()">Print Values</button></div>`,
  directives: [FORM_DIRECTIVES, NgFor]
})
export class AppComponent {
  objArray:TestObject[] = [{name: 'foo', value: 1}, {name: 'bar', value: 1}];
  objValue1:TestObject = this.objArray[1];
  objValue2:TestObject = this.objArray[1];
  objValue3:TestObject = this.objArray[1];
  objValue4:TestObject = this.objArray[1];

  strArray:string[] = this.objArray.map((obj:TestObject) => obj.name);
  strValue:string = this.strArray[1];

  stringify(o:any):string {
    return JSON.stringify(o);
  }

  updateObjValue2(event:Event):void {
    const value:string = (<HTMLSelectElement>event.srcElement).value;

    this.objValue2 = this.objArray.find((obj:TestObject) => obj.name === value);
  }

  updateObjValue3(event:Event):void {
    const value:string = (<HTMLSelectElement>event.srcElement).value;

    this.objValue3 = this.objArray.find((obj:TestObject) => obj.name === value);
  }

  updateObjValue4(event:string):void {
    this.objValue4 = JSON.parse(event);
    // if there were functions on TestObject, JSON.parse wouldn't be enough; we'd need this:
    //this.objValue4 = this.objArray.find((obj:TestObject) => obj.name === JSON.parse(event).name);
  }

  printValues():void {
    console.log('strValue', this.strValue);
    console.log('objValue1', this.objValue1);
    console.log('objValue2', this.objValue2);
    console.log('objValue3', this.objValue3);
    console.log('objValue4', this.objValue4);
  }
}

I can do (4) or (5) if that's the intended way, but they seem pretty clunky. Is there another approach? Am I just too early in the alpha? Did I do something silly?

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions