Search code examples
angulardrop-down-menuundefinedangular-ngmodelchange

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?


Solution

  • 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