私たちのアプリケーションは成長しています。 このパートでは、データをコンポーネントに転送するだけでなく、再利用可能なコンポーネントに焦点を当てます。 ヒーローのリストを個別のコンポーネントに分け、このコンポーネントを再利用に適したものにしましょう。
私たちが泊まった場所
ヒーローツアーを続ける前に、プロジェクトが次の構造を持っていることを確認しましょう。 そうでない場合は、前の章に戻る必要があります。
angular2-tour-of-heroes app app.component.ts main.ts node_modules ... typings ... index.html package.json tsconfig.json typings.json
コード変換とアプリケーション実行をサポート
TypeScriptコンパイラを実行して、ファイルの変更を監視し、すぐにコンパイルし、Webサーバーを起動する必要があります。 入力することでこれを行います
npm start
これにより、ヒーローツアーの作成を継続している間、アプリケーションが実行され続けます。
ヒーロー詳細情報コンポーネントの作成
ヒーローのリストとヒーローに関する詳細情報は、同じコンポーネントの1つのファイルにあります。 これまでのところ、それらは小さいですが、それぞれ成長することができます。 そのうちの1つに対して新しい要件を取得できます。これには、1つだけを変更する必要があり、もう1つは変更しません。 ただし、各変更には2つのコンポーネントのエラーのリスクが伴い、テストが2倍になります。 ヒーローに関する詳細情報をアプリケーションの他の場所で再利用する必要がある場合、ヒーローのリストを取得する必要があります。
私たちの現在の構成要素は、 単一の責任原則に違反しています。 この教材は単なるレッスンにすぎませんが、特に難しいことではないので、正しく行うことができます。 さらに、このプロセスでは、Angularでアプリケーションを構築する方法について詳しく学習します。
ヒーローに関する詳細情報を独自のコンポーネントに抽出してみましょう。
主人公に関する詳細情報を個別に
以下に示すように、 hero-detail.component.ts
という新しいファイルをapp
フォルダーに追加し、 hero-detail.component.ts
を作成します。
hero-detail.component.ts(初期バージョン)
import {Component, Input} from 'angular2/core'; @Component({ selector: 'my-hero-detail', }) export class HeroDetailComponent { }
命名規則
どのクラスがコンポーネントであるか(クラス名ごと)およびどのファイルがコンポーネントを含むか(ファイル名ごと)を一目で理解したいと思います。
AppComponent
という名前のファイルにapp.component.ts
あり、新しいHeroDetailComponent
はhero-detail.component.ts
という名前のファイルにあることに注意してください。
すべての複合クラス名は「Component」で終わります。 複合ファイル名はすべて「.component」で終わります。
ファイル名を「ダッシュ付き小文字」(kebab-case)に変換するため、サーバーまたはバージョン管理システムでの大文字と小文字の区別については心配しません。
上記のコードを検討してください。
Angularデコレータ-ComponentおよびInput
インポートすることから始めました。すぐに必要になるからです。
次に、 @Component
デコレータを使用してメタデータを作成します。ここで、コンポーネント要素を識別するセレクタの名前を指定します。 次に、クラスをエクスポートして、他のコンポーネントで使用できるようにします。
ここで終了したら、 AppComponent
インポートして、対応する要素を作成します。
<my-hero-detail>
。
ヒーロー詳細テンプレート
現在、 ヒーローとヒーローの詳細ビューはAppComponent
1つのテンプレートに結合されていAppComponent
。 ヒーロー詳細のコンテンツをAppComponent
からクリップして、新しいHeroDetailComponent
テンプレートHeroDetailComponent
貼り付けましょう。
以前は、 AppComponent
selectedHero.name
プロパティをバインドしました。 HeroDetailComponent
には、 HeroDetailComponent
プロパティではなく、 hero
プロパティがあります。 したがって、新しいテンプレート全体でselectedHero
をhero
置き換えます。 これが唯一の変更です。 結果は次のようになります。
hero-detail.component.ts(テンプレート)
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> `
これで、ヒーローに関する詳細情報のマークアップはHeroDetailComponent
のみ存在します。
ヒーロープロパティの追加
上記で説明したhero
プロパティをコンポーネントクラスに追加します。
hero: Hero;
ああ、ああ。 hero
プロパティをHero
タイプとして宣言しましたが、ヒーロークラスはapp.component.ts
ファイルにあります。 2つのコンポーネントがあり、それぞれが独自のファイルでHero
クラスを参照する必要があります。
Hero
クラスをapp.component.ts
から独自のhero.ts
ファイルに移動することにより、この問題を解決します。
hero.ts(エクスポートされたヒーロークラス)
export class Hero { id: number; name: string; }
両方のコンポーネントファイルで参照する必要があるため、 hero.ts
からHero
クラスをエクスポートします。 app.component.ts
およびhero-detail.component.ts
上部に次のimportステートメントを追加します。
hero-detail.component.tsおよびapp.component.ts(ヒーロークラスのインポート)
import {Hero} from './hero';
ヒーロープロパティはインバウンドです。
HeroDetailComponent
コンポーネントに表示するヒーローをHeroDetailComponent
する必要があります。 誰が彼にそれを言うのでしょうか? AppComponent
親!
AppComponent
、表示するヒーローを知っています。ユーザーがリストから選択したヒーローです。 ユーザーの選択はselectedHero
プロパティにあります。
AppComponent
テンプレートを更新して、 AppComponent
プロパティをHeroDetailComponent
hero
プロパティにHeroDetailComponent
ます。 バインディングは次のようになります。
<my-hero-detail [hero]="selectedHero"></my-hero-detail>
hero
プロパティがターゲットプロパティであることに注意してください-(=)の左側に角かっこで囲まれています。
Angularでは、宣言されたターゲットプロパティが受信プロパティである必要があります。 そうしないと、Angularはバインドを拒否し、エラーメッセージを表示します。
ここでは、入力プロパティについて詳しく説明します 。また、 ターゲットプロパティにこの特別なアプローチが必要な理由を説明しますが、 ソースプロパティには必要ありません。
hero
がインバウンドであることを示す方法はいくつかあります。 先ほどインポートした@Input
デコレータでhero
プロパティに注釈を付けることで、これを優先する方法にします。
@Input() hero: Hero;
@Input()
デコレータの詳細については、 属性ディレクティブの章を@Input()
。
AppComponentの更新
AppComponent
に戻って、彼にHeroDetailComponent
使用方法を教えましょう。
まず、 HeroDetailComponent
をインポートして、参照できるようにします。
import {HeroDetailComponent} from './hero-detail.component';
Hero Detailのコンテンツを削除したテンプレート内の場所を見つけ、 HeroDetailComponent
を表す要素タグを追加します。
<my-hero-detail></my-hero-detail>
my-hero-detailは、HeroDetailComponent
メタデータのselector
プロパティで設定した名前です。
次のように、 AppComponent
コンポーネントのselectedHero
プロパティをAppComponent
コンポーネントのhero
プロパティに関連付けるまで、これら2つのコンポーネントは調整されません。
<my-hero-detail [hero]="selectedHero"></my-hero-detail>
AppComponent
テンプレートは次のようになります。
app.component.ts(テンプレート)
template:` <h1>{{title}}</h1> <h2>My Heroes</h2> <ul class="heroes"> <li *ngFor="#hero of heroes" [class.selected]="hero === selectedHero" (click)="onSelect(hero)"> <span class="badge">{{hero.id}}</span> {{hero.name}} </li> </ul> <my-hero-detail [hero]="selectedHero"></my-hero-detail> `,
バインディングのおかげで、 HeroDetailComponent
はAppComponent
からヒーローを受け取り、リストの下にこのヒーローの詳細情報を表示する必要があります。 この情報は、ユーザーが新しいヒーローを選択するたびに更新する必要があります。
これはまだ起こっていません!
ヒーローのリストをクリックします。 情報なし。 「Browser Developer Tools」コンソールでエラーを探しています。 エラーはありません。
Angularは新しいタグを無視しているようです。 これは、彼が新しいタグを本当に無視しているためです。
ディレクティブの配列
ブラウザは、未知のHTMLタグと属性を無視します。 角度も同じです。
HeroDetailComponent
をインポートし、テンプレートで使用しましたが、Angularに通知しませんでした。
directives
配列のメタデータにこのコンポーネントをリストすることで、このAngularについて説明します。 このプロパティの配列を@Component
構成の下部、 template
とstyles
直後に追加しstyles
。
directives: [HeroDetailComponent]
稼いだ!
ブラウザでアプリケーションを表示すると、ヒーローのリストが表示されます。 ヒーローを選択すると、彼に関する詳細情報が表示されます。
基本的な変更点は、このHeroDetailComponent
コンポーネントを使用して、アプリケーションのどこかでヒーローに関する詳細情報を表示できることです。
最初の再利用可能なコンポーネントを作成しました!
アプリケーション構造の概要
この章でリファクタリングした後、次のプロジェクト構造があることを確認しましょう。
angular2-tour-of-heroes app app.component.ts hero.ts hero-detail.component.ts main.ts node_modules ... typings ... index.html package.json tsconfig.json typings.json
この章で説明したコードファイル。
import {Component, Input} from 'angular2/core'; import {Hero} from './hero'; @Component({ selector: 'my-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; }
import {Component} from 'angular2/core'; import {Hero} from './hero'; import {HeroDetailComponent} from './hero-detail.component'; @Component({ selector: 'my-app', template:` <h1>{{title}}</h1> <h2>My Heroes</h2> <ul class="heroes"> <li *ngFor="#hero of heroes" [class.selected]="hero === selectedHero" (click)="onSelect(hero)"> <span class="badge">{{hero.id}}</span> {{hero.name}} </li> </ul> <my-hero-detail [hero]="selectedHero"></my-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; } `], directives: [HeroDetailComponent] }) export class AppComponent { title = 'Tour of Heroes'; heroes = HEROES; selectedHero: Hero; onSelect(hero: Hero) { this.selectedHero = hero; } } var 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" } ];
export class Hero { id: number; name: string; }
歩いた道
作成したものをまとめましょう。
- 再利用可能なコンポーネントを作成しました。
- コンポーネントに入力を許可する方法を学びました。
- 親コンポーネントを子コンポーネントにバインドする方法を学びました。
- ディレクティブ配列で必要なアプリケーションディレクティブを宣言する方法を学びました。
今後のパス
ヒーローツアーは、共有コンポーネントでの再利用に適しています。
AppComponent
ヒーローデータを取得します(スタブを使用して取得します)。 これは最良のオプションではありません。 データへのアクセスをリファクタリングし、別のサービスでデータの受信を取り出し、このデータを必要とするコンポーネントとこのサービスを共有する必要があります。
次の章でサービスの作成方法を学びます。