Angular2: Не удается прочитать свойство 'name' из undefined

Я начинаю изучать Angular2. Я слежу за учебником Heroes, представленным в angular.io. Все работало нормально до тех пор, пока его не раздражало беспорядок HTML с использованием шаблона, вместо этого я использовал URL-адрес шаблона и переместил HTML в файл с именем hero.html. Создается ошибка: "Невозможно прочитать свойство" имя "undefined". Как ни странно, переменная героев, которая указывает на массив объектов, может быть доступна, так что ngFor будет производить правильное количество тегов "li" в соответствии с количеством объектов в массиве. Однако доступ к данным объектов массива невозможен. Кроме того, даже простая переменная, содержащая некоторый текст, не будет отображаться с помощью {{}} скобок в HTML (см. Приведенный код).

app.component.ts

import { Component } from '@angular/core';
@Component({
  selector: 'my-app',
  templateUrl: './hero.html',
  styleUrls:['./styles.css']
})

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

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

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

const HEROES: Hero[] = [
   { id: 1, name: 'Mr. Nice' },
   { id: 2, name: 'Narco' },
   { id: 3, name: 'Bombasto' },
   { id: 4, name: 'Celeritas' },
   { id: 5, name: 'Magneta' },
   { id: 6, name: 'RubberMan' },
   { id: 7, name: 'Dynama' },
   { id: 8, name: 'Dr IQ' },
   { id: 9, name: 'Magma' },
   { id: 10, name: 'Tornado' }
];

hero.html

<h1>{{title}}</h1>
<h2>My Heroes</h2>
<ul class="heroes">
  <li *ngFor="let hero of heroes">
    <span class="badge">{{hero.id}}</span> {{hero.name}}
  </li>
</ul>
<h2>{{hero.name}} details!</h2>
<div>
    <label>id: </label>{{hero.id}}
</div>
<div>
    <label>name: </label>
    <input [(ngModel)]="selectedHero.name" placeholder="name">
<div>

Вот фото:

введите описание изображения здесь

Ответы

Ответ 1

Выбранная переменнаяHero имеет значение null в шаблоне, поэтому вы не можете привязать выбранноеHero.name как есть. Вам нужно использовать оператор elvis для этого случая:

<input [ngModel]="selectedHero?.name" (ngModelChange)="selectedHero.name = $event" />

Разделение [(ngModel)] в [ngModel] и (ngModelChange) также необходимо, потому что вы не можете назначить выражение, которое использует оператор elvis.

Я также думаю, что вы хотите использовать:

<h2>{{selectedHero?.name}} details!</h2>

вместо:

<h2>{{hero.name}} details!</h2>

Ответ 2

Вам просто нужно было прочитать немного дальше, и вы бы познакомились с структурной директивой * ngIf.

selectedHero.name еще не существует, поскольку пользователю еще предстоит выбрать героя, чтобы он возвращал undefined.

<div *ngIf="selectedHero">
  <h2>{{selectedHero.name}} details!</h2>
  <div><label>id: </label>{{selectedHero.id}}</div>
  <div>
    <label>name: </label>
    <input [(ngModel)]="selectedHero.name" placeholder="name"/>
  </div>
</div>

Директива * ngIf сохраняет selectedHero от DOM до тех пор, пока она не будет выбрана и поэтому станет правдой.

Этот документ помог мне понять структурные директивы.

Ответ 3

Вы получили эту ошибку, потому что следовали плохо написанным указаниям в учебнике Heroes. Я столкнулся с тем же.

В частности, под заголовком "Отображать имена героев в шаблоне" указано:

Чтобы отобразить имена героев в неупорядоченном списке, вставьте следующее фрагмент HTML под заголовком и над деталями героя.

за которым следует этот код:

<h2>My Heroes</h2>
<ul class="heroes">
  <li>
    <!-- each hero goes here -->
  </li>
</ul>

Он не рекомендует вам заменять предыдущий детальный код, и он должен. Вот почему мы остались:

<h2>{{hero.name}} details!</h2>

вне нашего *ngFor.

Однако, если вы прокрутите страницу вниз, вы столкнетесь со следующим:

Шаблон для отображения героев должен выглядеть так:

<h2>My Heroes</h2>
<ul class="heroes">
  <li *ngFor="let hero of heroes">
    <span class="badge">{{hero.id}}</span> {{hero.name}}
  </li>
</ul>

Обратите внимание на отсутствие элементов детали из предыдущих усилий.

Ошибка, подобная этой автору, может привести к довольно дикому гусиному преследованию. Надеюсь, этот пост помогает другим избежать этого.

Ответ 4

Чтобы избежать этого, вы также можете инициализировать элемент selectedHero вашего компонента пустым объектом (вместо того, чтобы оставить его неопределенным).

В вашем примере кода это выглядело бы примерно так:

export class AppComponent {
  title = 'Tour of Heroes';
  heroes = HEROES;
  selectedHero:Hero = new Hero();

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

Ответ 5

Эта строка

<h2>{{hero.name}} details!</h2>

находится вне *ngFor, и нет hero, поэтому hero.name терпит неудачу.

Ответ 6

Это сработало для меня:

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

   public Hero(i: number, n: string){
     this.id = 0;
     this.name = '';
   }
 }

и убедитесь, что вы также инициализировали selectedHero

selectedHero: Hero = new Hero();