mariocatch
mariocatch

Reputation: 8703

Defaulting an Angular2 <option> in a <select> with two-way binding

I'd like to setup a <select> with a default <option> using HTML, and populating the rest of the <select> with two way binding to my model.

HTML:

<select class="form-control" required
        [(ngModel)]="model.product"
        ngControl="product">
    <option value="">Select a product</option>
    <option *ngFor="#product of products" [value]="product">{{product}}</option>
</select>

Model:

products: string[] = [
    'Foo',
    'Bar'
];

What's happening now is my the select is binding to the model, which is:

model: Entry = new Entry();

So, there's no default value in the product property, thus the <select> binds to nothing. I mean, this is working as expected.

I'm trying to figure out how to get a default value to show up in the <select> though, and I'd like to do that without hard coding a UI value in my model and defaulting that in a new instance of my model. Does Angular 2 have a way to deal with this?

Edit:

The result HTML should look like:

<select>
  <option value>Select a product</option>
  <option value="foo">Foo</option>
  <option value="bar">Bar</option>
</select>

Upvotes: 26

Views: 29211

Answers (6)

Pranav Labhe
Pranav Labhe

Reputation: 1953

You are approaching in right direction. One thing that needs to consider is that null value of model is being compared with empty string value of default option. So if you compare and set null value of your default display text then it'll work fine. I have set as above and it's working for me !

<select class="form-control" 
        required
        [(ngModel)]="model.product"
        ngControl="product">
    <option value="model.product === null ? null : ''">
            Select a product
    </option>
    <option *ngFor="#product of products" 
            [value]="product">{{product}}</option>
</select>

Upvotes: 0

Izabela Furdzik
Izabela Furdzik

Reputation: 521

I have done it this way and it works for me:

<select name="Products" #Products="ngModel" [(ngModel)]="products">
  <option [ngValue]="products">-- Select product --</option>
  <option *ngFor="let p of productsList" [ngValue]="p">{{p.name}}</option>
</select>

[ngValue]="products" indicates to [(ngModel)]="products".

Before this I had <option value="">-- Select product --</option> but then it renderd to: blank option, -- Select product -- and list of my products.

Upvotes: 11

Negin
Negin

Reputation: 2394

I exactly had the same issue, we can just bind the ngModel to the value :

<option *ngFor="#product of products" [value]="model.product">{{product}}</option>

Upvotes: 0

Maike Mota
Maike Mota

Reputation: 190

On beta 14 (see: Angula2-beta.14 Changlog), angular adds the support for Object in select tags:

<select class="form-control" required [(ngModel)]="model.product">
  <option *ngFor="#prod of products" 
   [ngValue]="prod">
     {{prod.name}}
  </option>
</select>

instead of [value], you can use [ngValue] to bind an object to ngModel in select tag.

You can see a example on Plunker with default value.

The attribute selected isn't working, I'm trying to fix this on my project with some workaround!

Hope that can be useful.

Upvotes: 2

kemsky
kemsky

Reputation: 15279

In fact, you could create Directive to append default option and handle two-way binding:

import {Directive, ElementRef, Input, OnInit, Output, EventEmitter, HostListener} from 'angular2/core';

/**
 * <select [ngSelect]="'select number...'" [(ngSelectModel)]="model" >
 *    <option *ngFor="#item of ['1', '2', '3']" [value]="item">{{item}}</option>
 * </select>
 */
@Directive({
    selector: '[ngSelect]'
})
export class DefaultOption implements OnInit
{
    private _el:HTMLSelectElement;

    @Input('ngSelect')
    private defaultText:string;

    @Output()
    private ngSelectModelChange:EventEmitter = new EventEmitter();

    private _ngSelectModel:any;

    constructor(el:ElementRef)
    {
        this._el = el.nativeElement;
    }

    @Input()
    public get ngSelectModel():any
    {
        return this._ngSelectModel;
    }

    public set ngSelectModel(value:any)
    {
        this._ngSelectModel = value;
        if (this.ngSelectModel !== undefined)
        {
            this._el.value = this.ngSelectModel;
        }
        else
        {
            this._el.value = '';
        }
    }

    protected ngOnInit():any
    {
        var element:HTMLOptionElement = document.createElement('option');
        element.text = this.defaultText;
        element.value = '';
        this._el.insertBefore(element, this._el.firstChild);

        //delayed execution
        setTimeout(()=>
        {
            if (this.ngSelectModel !== undefined)
            {
                this._el.value = this.ngSelectModel;
            }
            else
            {
                this._el.value = '';
            }
        }, 0);
    }

    @HostListener('change', ['$event.target.value'])
    private onInput(value):void
    {
        if(value !== '')
        {
            this.ngSelectModelChange.emit(value);
        }
        else
        {
            this.ngSelectModelChange.emit(undefined);
        }
    }
}

Upvotes: 3

Mark Rajcok
Mark Rajcok

Reputation: 364727

I don't think Angular has a way to deal with this. You'll have to do some work:

<select class="form-control" required [(ngModel)]="model.product">
  <option *ngFor="#product of [defaultStr].concat(products)" 
   [value]="product === defaultStr ? null : product">
     {{product}}
  </option>
</select>
defaultStr = 'Select a product';
model = { product: null };
products: string[] = [
  'Foo',
  'Bar'
];

Plunker

Since you are using NgModel to bind the selected value, you have to set the bound property to the default value initially, which is null, otherwise that option won't be selected by default:

model = { product: null };

With null, I noticed that the HTML is

<option value="null">Select a product</option>

So, you may prefer to use an empty string '' instead of null:

[value]="product === defaultStr ? '' : product"
model = { product: '' };

Then the HTML is

<option value="">Select a product</option>

Upvotes: 13

Related Questions