Jay Smith
Jay Smith

Reputation: 2480

Why [(ngModel)] breaks all template in angular 4?

This is my first component in angular. Here it is:

import { Component } from '@angular/core';

export class Hero{
    id:number;
    name:string;
}

const HEROES: Hero[]=[
  {id:11,name:'Mr. Nice'},
  {id:12,name:'Arco'},
  {id:13,name:'Gillette'},
  {id:14,name:'Celeritas'},
  {id:15,name:'Magneta'},
  {id:16,name:'RubberMan'},
  {id:17,name:'Dynama'},
  {id:18,name:'Dr. Iq'},
  {id:19,name:'Magma'},
  {id:20,name:'TOrnado'}
];


@Component({
  selector: 'my-app',
  template: `<h1>{{title}}</h1>
            <h2>My heroes</h2>
            <ul class="heroes">
                <li *ngFor="let hero of heroes" (click)="onSelect(hero)">
                    <span class="badge">{{hero.id}}</span> {{hero.name}}
                </li>
            </ul>
            <h2>Details of {{selectedHero.name}}</h2>
            <div><label>Id: </label>{{selectedHero.id}}</div>
            <div>
                <label>Name: </label>
                <input [(ngModel)]="selectedHero.name"/>
            </div>

  `,
  styles: [`

`]
})
export class AppComponent {
  title = 'Tour of Heroes';
  heroes = HEROES;
  selectedHero: Hero;

  onSelect(hero: Hero): void {
    this.selectedHero = hero;
  }
}

I deleted styles element because its big. My template doesn't bind heroes array, When I use this in template element:

<input [(ngModel)]="selectedHero.name"/>

But when I delete above input, all heroes from array are correctly displayed in a <ul> list.

Why is it?

Upvotes: 2

Views: 651

Answers (2)

P.S.
P.S.

Reputation: 16384

It's because Angular can't read selectedHero.name while there is no selected hero. You can (and need to) show this input only when you have selectedHero using *ngIf directive, like this:

<input *ngIf="selectedHero" [(ngModel)]="selectedHero.name"/>

And not only for input - for every element where you're using selectedHero:

<h2 *ngIf="selectedHero">Details of {{selectedHero.name}}</h2>
<div *ngIf="selectedHero"><label>Id: </label>{{selectedHero.id}}</div>
<div *ngIf="selectedHero">
  <label>Name: </label>
  <input [(ngModel)]="selectedHero.name"/>
</div>

Or better add a wrapper with one *ngIf directive:

<div *ngIf="selectedHero">
  <h2>Details of {{selectedHero.name}}</h2>
  <div><label>Id: </label>{{selectedHero.id}}</div>
  <div>
    <label>Name: </label>
    <input [(ngModel)]="selectedHero.name"/>
  </div>
</div>

I remember, it was written somewhere in "Tour of Heroes" for Angular, it's not just my thoughts ;)

Upvotes: 1

tdragon
tdragon

Reputation: 3329

You do not set selectedHero property, but still you try to display and modify it. Move hero details to section and add ngIf to it to display it only when it's set:

<div *ngIf="selectedHero">
    <h2>Details of {{selectedHero.name}}</h2>
    <div><label>Id: </label>{{selectedHero.id}}</div>
    <div>
        <label>Name: </label>
        <input [(ngModel)]="selectedHero.name"/>
    </div>
</div>

Upvotes: 2

Related Questions