OreoFanatics
OreoFanatics

Reputation: 898

Angular 4 - ngModelChange throw cannot read property '...' of undefined during two select form binding

I have the following json model and want to have two select form (dropdown) in which the first dropdown will contain the title while the second dropdown contain the authors with the value depending on the which title to pick (first title has two, second has three).

 {
        "id": 1,
        "title": "bookA",
        "authors": [
            "authorA",
            "authorB"
        ]
},
 {
        "id": 2,
        "title": "bookB",
        "authors": [
            "authorA",
            "authorB",
            "authorC"
        ]
},

I am fairly new to Angular 4, but after searching around I come up with the following code in my html:

        <div class="form-group row">
            <label for="bookTitleField" class="col-sm-2 col-form-label">Title</label>
            <div class="col-sm-2">
                <select [(ngModel)]="currentInput.book.id" name="bookTitle" 
                (ngModelChange)="selectedBook=$event.target.value">
                    <option *ngFor="let b of books | async" value="{{b.id}}">{{b.title}}</option>
                </select>
            </div>
            <label for="bookAuthorField" class="col-sm-2 col-form-label">Author/label>
            <div class="col-sm-4">
                <select [(ngModel)]="currentInput.book.authors" *ngIf="currentInput.book.id" name="author">
                    <option *ngFor="let a of selectedBook" value="{{a.authors}}">{{a.authors}}</option>
                </select>
            </div>
        </div>

The first dropdown works as intented, however when the second one is clicked, an error was thrown:

ERROR TypeError: Cannot read property 'value' of undefined

Which part of the code is incorrect?

Upvotes: 2

Views: 2032

Answers (2)

AVJT82
AVJT82

Reputation: 73377

It is target that is undefined in your template. You would want to use $event.id to get the id value from the first dropdown. Also you want to use [ngValue] to bind the whole object in the first dropdown, so that you can show the authors in your next dropdown. So modify your code to something like this:

<select [(ngModel)]="chosenBook" name="bookTitle" (ngModelChange)="selectedBook.id = $event.id">
   <option *ngFor="let b of books | async" [ngValue]="b">{{b.title}}</option>
</select>

<label>Author</label>
<select [(ngModel)]="selectedBook.author">
  <option *ngFor="let a of chosenBook.authors">{{a}}</option>
</select>

Also remember to initialize your selectedBook and chosenBook so that you don't get an undefined error.

StackBlitz

Upvotes: 2

micronyks
micronyks

Reputation: 55443

Your code should work but it is hard to say right now. Other way is to use a function as show below,

<div *ngIf="books">              //added this condition
        <select [(ngModel)]="currentInput.book.id" name="bookTitle" 
                (ngModelChange)="setValue($event)">
</div>
setValue(model){
  this.selectedBook=model.target.value;
}

Upvotes: 0

Related Questions