Spec-Zone.ru › Angular 2

Многокомпонентное приложение

Переработать представление «мастер/детали» в отдельные компоненты.

В настоящее время AppComponent выполняет всё. Вначале оно отображало детали одного героя. Затем оно превратилось в форму «мастер/детали» с списком героев и деталями героя. Вскоре появятся новые требования и возможности. Нельзя постоянно добавлять функции к одному компоненту; это не поддерживается.

Вам нужно разбить его на подкомпоненты, каждый из которых будет сфокусирован на определённой задаче или рабочем процессе. Со временем AppComponent может стать простым оболочкой, которая будет содержать эти подкомпоненты.

В этой странице вы сделаете первый шаг в этом направлении, выделив детали героя в отдельный, повторно используемый компонент. После завершения приложение должно выглядеть так, как показано в этом живом примере.

Что вы сделали ранее

Прежде чем начать работу на этой странице, убедитесь, что у вас есть следующая структура из предыдущего раздела Тура по героям. В противном случае вернитесь к предыдущим страницам.

angular-tour-of-heroes
  src
    app
      app.component.ts
      app.module.ts
    main.ts
    index.html
    styles.css
    systemjs.config.js
    tsconfig.json
  node_modules ...
  package.json

Продолжайте транспилирование и выполнение приложения во время разработки Тура по героям, введя команду npm start в окне терминала как вы делали раньше.

Создайте компонент деталей героя

Добавьте файл с именем hero-detail.component.ts в папку app/. Этот файл будет содержать новый HeroDetailComponent.

Имя файла и компонента соответствуют стандарту, описанному в руководстве по стилю Angular руководство по стилю.

  • Имя класса компонента должно быть написано с использованием верхнего регистрового стиля и оканчиваться словом "Component". Класс компонента деталей героя — HeroDetailComponent.

  • Имя файла компонента должно быть написано в нижнем регистровом стиле, каждое слово разделено дефисом и оканчивается на .component.ts. Класс HeroDetailComponent находится в файле hero-detail.component.ts.

Начните писать HeroDetailComponent следующим образом:

app/hero-detail.component.ts (начальная версия)

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

@Component({
  selector: 'hero-detail',
})
export class HeroDetailComponent {
}

Для определения компонента всегда импортируется символ Component.

Декоратор @Component предоставляет метаданные Angular для компонента. Имя селектора CSS, hero-detail, будет соответствовать имени тега элемента, который идентифицирует этот компонент в шаблоне родительского компонента. В конце этой страницы вы добавите элемент <hero-detail> в шаблон AppComponent.

Всегда export класс компонента, так как вы всегда import его где-то еще.

Шаблон деталей героя

Чтобы перенести представление деталей героя в HeroDetailComponent, вырежьте содержимое деталей героя из нижней части шаблона AppComponent и вставьте его в новое свойство template в метаданных @Component.

В HeroDetailComponent имеется герой, а не выбранный герой. Замените слово «selectedHero» на слово «hero» во всём шаблоне. После завершения новый шаблон должен выглядеть следующим образом:

src/app/hero-detail.component.ts (шаблон)

@Component({
  selector: 'hero-detail',
  template: `
    <div *ngIf="hero">
      <h2>{{hero.name}} details!</h2>
      <div><label>id: </label>{{hero.id}}</div>
      <div>
        <label>name: </label>
        <input [(ngModel)]="hero.name" placeholder="name"/>
      </div>
    </div>
  `
})

Добавьте свойство hero

Шаблон HeroDetailComponent привязан к свойству компонента hero. Добавьте это свойство в класс HeroDetailComponent следующим образом:

src/app/hero-detail.component.ts (свойство hero)

hero: Hero;

Свойство hero имеет тип экземпляра Hero . Класс Hero всё ещё находится в файле app.component.ts . Теперь два компонента должны ссылаться на класс Hero . Руководство по стилю Angular рекомендует один класс на файл.

Переместите класс Hero из app.component.ts в собственный файл hero.ts.

src/app/hero.ts

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

Теперь, когда класс Hero находится в отдельном файле, AppComponent и HeroDetailComponent должны его импортировать. Добавьте следующее import утверждение в начале обоих файлов app.component.ts и hero-detail.component.ts.

import { Hero } from './hero';

Свойство hero является свойством ввода

Позже на этой странице родительский компонент AppComponent сообщит дочернему компоненту HeroDetailComponent какой герой должен отображаться, связав его selectedHero со свойством hero компонента HeroDetailComponent. Привязка будет выглядеть так:

<hero-detail [hero]="selectedHero"></hero-detail>

Использование квадратных скобок вокруг свойства hero слева от знака равенства (=) делает его целью выражения привязки свойства. Вы должны объявить целевое свойство привязки как свойство ввода. В противном случае Angular отклонит привязку и выдаст ошибку.

Во-первых, измените инструкцию импорта @angular/core , чтобы включить символ Input.

src/app/hero-detail.component.ts (фрагмент)

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

Затем объявите, что hero является свойством ввода, поместив перед ним декоратор @Input , который вы импортировали ранее.

src/app/hero-detail.component.ts (фрагмент)

@Input() hero: Hero;

Дополнительную информацию о свойствах ввода можно найти на странице Директивы атрибутов.

Всё. Свойство hero — единственное, что есть в классе HeroDetailComponent.

export class HeroDetailComponent {
  @Input() hero: Hero;
}

Всё, что он делает, это получает объект героя через свойство ввода hero и затем связывается с этим свойством в своём шаблоне.

Вот полный HeroDetailComponent.

src/app/hero-detail.component.ts

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

import { Hero } from './hero';
@Component({
  selector: 'hero-detail',
  template: `
    <div *ngIf="hero">
      <h2>{{hero.name}} details!</h2>
      <div><label>id: </label>{{hero.id}}</div>
      <div>
        <label>name: </label>
        <input [(ngModel)]="hero.name" placeholder="name"/>
      </div>
    </div>
  `
})
export class HeroDetailComponent {
  @Input() hero: Hero;
}

Объявление HeroDetailComponent в AppModule

Каждый компонент должен быть объявлен в одном — и только в одном — модуле Angular.

Откройте app.module.ts в вашем редакторе и импортируйте HeroDetailComponent , чтобы вы могли на него ссылаться.

src/app/app.module.ts

import { HeroDetailComponent } from './hero-detail.component';

Добавьте HeroDetailComponent в массив declarations модуля.

src/app/app.module.ts

declarations: [
  AppComponent,
  HeroDetailComponent
],

В общем, массив declarations содержит список компонентов, труб и директив приложения, которые принадлежат модулю. Компонент должен быть объявлен в модуле, прежде чем другие компоненты смогут на него сослаться. Этот модуль объявляет только два компонента приложения: AppComponent и HeroDetailComponent.

Дополнительную информацию о модулях Angular можно найти в руководстве NgModules.

Добавление HeroDetailComponent к AppComponent

Компонент AppComponent по-прежнему является представлением «мастер/детали». Раньше он отображал детали героя самостоятельно, прежде чем вы вырезали эту часть шаблона. Теперь он делегирует их HeroDetailComponent.

Обратите внимание, что hero-detail является селектором CSS selector в метаданных HeroDetailComponent. Это имя тега элемента, который представляет HeroDetailComponent.

Добавьте элемент <hero-detail> в нижнюю часть шаблона AppComponent , где раньше отображались детали героя.

Синхронизируйте родительский компонент AppComponent с HeroDetailComponent , связав свойство selectedHero компонента AppComponent со свойством hero компонента HeroDetailComponent.

app.component.html (фрагмент)

<hero-detail [hero]="selectedHero"></hero-detail>

Теперь каждый раз, когда selectedHero меняется, HeroDetailComponent получает нового героя для отображения.

Переработанный шаблон AppComponent должен выглядеть следующим образом:

app.component.ts (фрагмент)

template: `
  <h1>{{title}}</h1>
  <h2>My Heroes</h2>
  <ul class="heroes">
    <li *ngFor="let hero of heroes"
      [class.selected]="hero === selectedHero"
      (click)="onSelect(hero)">
      <span class="badge">{{hero.id}}</span> {{hero.name}}
    </li>
  </ul>
  <hero-detail [hero]="selectedHero"></hero-detail>
`,

Что изменилось?

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

Переработка исходного компонента AppComponent в два компонента дает преимущества как сейчас, так и в будущем:

  1. Вы упростили AppComponent , уменьшив его ответственность.

  2. Вы можете развить HeroDetailComponent в богатый редактор героев, не затрагивая родительский компонент AppComponent.

  3. Вы можете развить AppComponent , не затрагивая представление деталей героя.

  4. Вы можете повторно использовать HeroDetailComponent в шаблоне будущего родительского компонента.

Обзор структуры приложения

Убедитесь, что у вас есть следующая структура:

angular-tour-of-heroes
  src
    app
      app.component.ts
      app.module.ts
      hero.ts
      hero-detail.component.ts
    main.ts
    index.html
    styles.css
    systemjs.config.js
    tsconfig.json
  node_modules ...
  package.json

Вот файлы кода, обсуждавшиеся на этой странице.

src/app/hero-detail.component.ts
import { Component, Input } from '@angular/core';

import { Hero } from './hero';
@Component({
  selector: 'hero-detail',
  template: `
    <div *ngIf="hero">
      <h2>{{hero.name}} details!</h2>
      <div><label>id: </label>{{hero.id}}</div>
      <div>
        <label>name: </label>
        <input [(ngModel)]="hero.name" placeholder="name"/>
      </div>
    </div>
  `
})
export class HeroDetailComponent {
  @Input() hero: Hero;
}
src/app/app.component.ts
import { Component } from '@angular/core';

import { Hero } from './hero';

const HEROES: Hero[] = [
  { id: 11, name: 'Mr. Nice' },
  { id: 12, name: 'Narco' },
  { id: 13, name: 'Bombasto' },
  { 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"
        [class.selected]="hero === selectedHero"
        (click)="onSelect(hero)">
        <span class="badge">{{hero.id}}</span> {{hero.name}}
      </li>
    </ul>
    <hero-detail [hero]="selectedHero"></hero-detail>
  `,
  styles: [`
    .selected {
      background-color: #CFD8DC !important;
      color: white;
    }
    .heroes {
      margin: 0 0 2em 0;
      list-style-type: none;
      padding: 0;
      width: 15em;
    }
    .heroes li {
      cursor: pointer;
      position: relative;
      left: 0;
      background-color: #EEE;
      margin: .5em;
      padding: .3em 0;
      height: 1.6em;
      border-radius: 4px;
    }
    .heroes li.selected:hover {
      background-color: #BBD8DC !important;
      color: white;
    }
    .heroes li:hover {
      color: #607D8B;
      background-color: #DDD;
      left: .1em;
    }
    .heroes .text {
      position: relative;
      top: -3px;
    }
    .heroes .badge {
      display: inline-block;
      font-size: small;
      color: white;
      padding: 0.8em 0.7em 0 0.7em;
      background-color: #607D8B;
      line-height: 1em;
      position: relative;
      left: -1px;
      top: -4px;
      height: 1.8em;
      margin-right: .8em;
      border-radius: 4px 0 0 4px;
    }
  `]
})
export class AppComponent {
  title = 'Tour of Heroes';
  heroes = HEROES;
  selectedHero: Hero;

  onSelect(hero: Hero): void {
    this.selectedHero = hero;
  }
}
src/app/hero.ts
export class Hero {
  id: number;
  name: string;
}
src/app/app.module.ts
import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule }   from '@angular/forms';

import { AppComponent }        from './app.component';
import { HeroDetailComponent } from './hero-detail.component';

@NgModule({
  imports: [
    BrowserModule,
    FormsModule
  ],
  declarations: [
    AppComponent,
    HeroDetailComponent
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

Путь, который вы прошли

Вот что вы достигли на этой странице:

  • Вы создали повторно используемый компонент.
  • Вы узнали, как заставить компонент принимать входные данные.
  • Вы научились объявлять необходимые директивы приложения в модуле Angular. Вы перечислили директивы в массиве NgModule декоратора declarations.
  • Вы научились связывать родительский компонент с дочерним компонентом.

Ваше приложение должно выглядеть так, как показано в этом живом примере.

Путь вперёд

Приложение Tour of Heroes стало более повторно используемым благодаря общим компонентам, но (модельные) данные всё ещё жёстко закодированы в AppComponent. Это не устойчиво. Доступ к данным следует переработать в отдельный сервис и разделить между компонентами, которые нуждаются в данных.

Вы узнаете, как создавать сервисы на странице следующего урока.

Следующий шаг

Сервисы

© 2010–2017 Google, Inc.
Licensed under the Creative Commons Attribution License 4.0.
https://v2.angular.io/docs/ts/latest/tutorial/toh-pt3.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API