Angular.ioにあるArchitectureについて簡単にまとめました。
モジュール
ルートモジュール
アプリケーションのrootととなるモジュール。アプリケーションをbootstrapする際に必要なモジュールたちをここで一括で読み込む。
フィーチャーモジュール
Root Moduleに読み込まれるモジュールたち。
NgModule
ディレクティブやサービスなどをひとまとまりにしたモジュールを宣言するためのAPI。
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@NgModule({
imports: [ BrowserModule ],
providers: [ Logger ],
declarations: [ AppComponent ],
exports: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
-
declarations
モジュール内で宣言されているコンポーネント、ディレクティブ、パイプを登録する。一度登録されればそのモジュール内ならどこでも使えるようになる。 -
providers
アプリケーション / モジュール全体で使用するサービスを登録する。ルートモジュールにprovider
を登録する場合は、 同じprovider
のインスタンスをアプリケーション全体で共有することが可能だが、コンポーネントのprovider
として登録した場合は、 コンポーネントが作成される度に新しいprovider
のインスタンスが作成される。 -
imports
自分のモジュールに別のモジュールを読み込むことができる。importsから読み込まれたモジュールからはexportsで指定されたものと、providersに登録されたものが読み込まれる。 -
exports
exportsプロパティにはコンポーネント、ディレクティブとパイプを指定することができる。 -
bootstrap
アプリケーションのエントリポイントとなるコンポーネントを指定する。
コンポーネント
ビューをコントロールするためのクラスやHTML、CSS、タグを定義する。
@Component({ // メタデータ
selector: 'my-app’, // ビューで使用するタグ名
template: '<h1>My First Angular 2 App</h1>' // HTMLテンプレート,
styleUrls: ['style.css’] // CSSファイル
})
export class AppComponent { } // コンポーネントクラス
テンプレート
コンポーネントで使用するビューファイル
<h2>Hero List</h2>
<p><i>Pick a hero from the list</i></p>
<ul>
<li *ngFor="let hero of heroes" (click)="selectHero(hero)">
{{hero.name}}
</li>
</ul>
<hero-detail *ngIf="selectedHero" [hero]="selectedHero"></hero-detail>
メタデータ
コンポーネント内で使用するメタデータを定義する。以下が主なメタデータ。
-
selector
HTMLでコンポーネントのインスタンスを埋め込むためのタグ名。 -
templateUrl
HTMLファイルの指定。 -
providers
コンポーネント内で使用するサービスを定義。
データバインティング
AngularはDOMとモデル間のデータバインティングを自動的に行ってくれる。
<li>{{hero.name}}</li>
<hero-detail [hero]="selectedHero"></hero-detail>
<li (click)="selectHero(hero)"></li>
-
[hero]="selectedHero"
親コンポーネントのselectedHero
プロパティを 子供コンポーネント(HeroDetailComponent
) にhero
をプロパティとしてバインディングさせている。 -
(click)="selectHero(hero)”
クリックされた時にselectedHero
を呼び出す。 -
双方向データバインティング (モデルからビューへ、ビューからモデルへ)
[…]
がインプットプロパティ、(…)
がアウトプットプロパティを意味している。双方向データバインディングをするにはngModel
を使うと簡単にできる。
<input [(ngModel)]=“hero.name”>
Directives
ディレクティブを使用することによって動的にDOMを変更することができる。コンポーネントもディレクティブの一種で、違いはテンプレートを含んでいるかいないか。
-
Structural Directive (構造化ディレクティブ)
DOMを削除したり、追加したりしてDOMのレイアウトを変更するもの。NgFor
やNgIf
など。 -
Attribute Directive (要素ディレクティブ)
HTML要素に変更を加えるディレクティブ。NgClass
、NgStyle
、NgModel
など。
<li *ngFor="let hero of heroes"></li>
<hero-detail *ngIf="selectedHero"></hero-detail>
サービス
シングルトンでコンポーネントやデータの状態を管理するために使われるクラス。複数のコンポーネントから呼び出されたりする。XHRのデータ取得や、バリデーションなどの複雑な処理はコンポーネントではなくサービスで行うのが一般的。コンポーネントの責務はビューとのバインディングなどのインタラクションのみにして lean(無駄がない)
にするのがよさげ。
export class HeroService {
private heroes: Hero[] = [];
constructor(
private backend: BackendService,
private logger: Logger) { }
getHeroes() {
this.backend.getAll(Hero).then( (heroes: Hero[]) => {
this.logger.log(`Fetched ${heroes.length} heroes.`);
this.heroes.push(...heroes); // fill cache
});
return this.heroes;
}
}
サービスはコンポーネントに一度加えられると、そのコンポーネントに付随する子供コンポーネントでも参照することができる。そのためルートコンポーネントで読み込ませたサービスのインスタンスは全てのコンポーネントで使用可能になる。
もし、複数のサービスのインスタンスを生成したい場合は以下のようにキャッシュさせる仕組みを整えてあげれば可能。
複数のHeroサービスのインスタンスを管理するHeroCheServiceを定義。
@Injectable()
export class HeroCacheService {
hero: Hero;
constructor(private heroService: HeroService) {}
fetchCachedHero(id: number) {
if (!this.hero) {
this.hero = this.heroService.getHeroById(id);
}
return this.hero;
}
}
依存型注入 (DI)
AngularDI機能を提供しており、主にサービスを使用するときにつかう。
コンポーネントの providers
プロパティに使用したいサービスを定義する
import { LoggerService } from './logger.service';
import { UserContextService } from './user-context.service';
import { UserService } from './user.service';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
providers: [ LoggerService, UserContextService, UserService ]
})
export class AppComponent {
/* . . . */
}
クラスのコンストラクタでインジェクトする
constructor(logger: LoggerService) {
logger.logInfo('Creating HeroBiosComponent');
}