Using [value] vs [ngValue] in Angular

When binding a dropdown/select list in Angular, you will come across two different ways of databinding, using [value] and using [ngValue]. They actually have a very simple difference. But let’s whip up an example first.

Imagine I have a component that holds a list of books :

export class AppComponent  {
  selectedBook : any;
  myBooks = [
    { id : 1, title : "War and Peace", author : "Leo Tolstoy" }, 
    { id : 2, title : "The Lord of the Rings", author : "J. R. R. Tolkien" }, 
    { id : 3, title : "Pride and Prejudice", author : "Jane Austen" }, 
  ];
}

Then in my view I have nothing but a dropdown, and I’m binding to the myBooks list. Also note that I’m using ngModel on the actual select to capture what is selected. And in this example, we are using the [value] attribute.

<select [(ngModel)]="selectedBook">
    <option *ngFor="let book of myBooks" [value]="book.id">{{book.title}}</option>
</select>
<br /><br />
The selected book is : {{selectedBook | json}}

At the end, I’m outputting my selected book in JSON format, just so we have some feedback on what we are selecting.

When I run this, everything works. When I select a book, I see the following :

Notice one thing however. Our “2” which is the id of our book is wrapped in quotes. This is the first thing you’ll notice about using [value]. [value] always binds as a string. Even if you pass it a number type, it will be coerced into a string.

You may not think this is a big deal at first. But if you are fully immersed in Typescripts strict typing, then this can cause unintended consequences because you may end up comparing strings and numbers (Or really any other type) when you never intended to.

That’s where [ngValue] comes in. Let’s change our binding to use [ngValue].

<select [(ngModel)]="selectedBook">
    <option *ngFor="let book of myBooks" [ngValue]="book.id">{{book.title}}</option>
</select>
<br /><br />
The selected book is : {{selectedBook | json}}

Now when we view our example in the browser :

No more quotes! This is because [ngValue] will bind any type and never try and convert things to strings.

Now obviously we are binding the id in our example. But often what happens is that you will require additional info about that item. So you end up querying the initial list to get say the title of the book. Ugh!

But remember, [ngValue] can bind keeping the original type in tact. So we can simply change our binding to be the entire book object.

<select [(ngModel)]="selectedBook">
    <option *ngFor="let book of myBooks" [ngValue]="book">{{book.title}}</option>
</select>
<br /><br />
The selected book is : {{selectedBook | json}}

Now, instead of just returning our id. Our selectedBook is the entire book object!

One final piece of the puzzle is that [value] will output HTML to the browser with the value intact like so :

<option value="1">War and Peace</option>

However if you use [ngValue], you instead get :

<option value="0: Object">War and Peace</option>

Because the binding is on an object. While this may not seem like a big deal (Who’s reading your HTML anyway?), it may be an issue if you use a browser automation tool such as selenium or cypress that is required to read the value of each option. So just keep that in mind!

In Summary :

  • Use [value] when you are binding to a string value and only a string value. Or you need access to the option value inside the HTML for automation purposes
  • Use [ngValue] when you are binding to any type and/or you want the entire option bound instead of just a string

Leave a Reply

Your email address will not be published. Required fields are marked *