Angular 2 RC5から導入されるNgModuleを使ってTodosを作ってみます。NgModuleの導入により@Componentで定義されていたものをNgModuleでまとめて定義することが可能となるようです。詳しくは @laco 氏のこちらを見て頂けると良いと思います。
ng2-info.github.io
変わるところとしては次のようなところのようです:
- bootstrap関数の呼び出し
- @Componentのdirectives
- @Componentのpipes
Todosを作ると少なくともコンポーネントが4つは作ると思います。
- 元となるコンポーネント: todos.ts
- 入力のコンポーネント: todos.input.ts
- 一覧のコンポーネント: todos.body.ts
- 詳細機能用のコンポーネント: todos.detail.ts
このコンポーネント郡をひとつにまとめるための機能がNgModuleかなといった感じです。ベストプラクティスは今度色々と出てくるとは思います。
具体的に見ていきます。
元となるコンポーネント: todos.ts
todos.ts
はtodos.input.ts
とtodos.body.ts
を利用しています。RC4までだとdirectives
を定義し、コンポーネント呼び出すように記述していましたが、これをNgModuleに記載します。したがってtodos.ts
からコンポーネントの宣言は削除されます。
before:
@Component({
selector: 'my-app',
template: `
<h2>Todos</h2>
<todos-input></todos-input>
<todos-body></todos-body>
`,
directives: [
TodosInputComponent,
TodosBodyComponent
],
providers: [TodoStore]
})
after:
import {Component} from '@angular/core';
import {TodosInputComponent} from './todos.input';
import {TodosBodyComponent} from './todos.body';
import {TodoStore} from './shared/todo.store';
@Component({
selector: 'my-app',
template: `
<h2>Todos</h2>
<todos-input></todos-input>
<todos-body></todos-body>
`,
providers: [TodoStore]
})
export class TodosComponent {}
入力のコンポーネント: todos.input.ts
入力コンポーネントは、関連するコンポーネントを持ちあわせていませんでした、しかし、NgForm
を利用していたためその記述をproviders
にしていましたがこれが無くなります。
import {Component, OnInit} from '@angular/core';
import {TodoStore} from './shared/todo.store';
import {Todo} from './shared/todo';
@Component({
selector: 'todos-input',
template: `
<form (ngSubmit)="onSubmit()" #todoForm="ngForm">
<input [(ngModel)]="todo.title" name="title" required placeholder="title">
<textarea [(ngModel)]="todo.desc" name="desc" required placeholder="desc"></textarea>
<button type=submit [disabled]="!todoForm.form.valid">登録</button>
</form>
`,
styles: [`
input {
width: 100%;
}
textarea {
width: 100%;
height: 7em;
}
`]
})
export class TodosInputComponent
implements OnInit {
private todo: Todo;
constructor (
private todoStore: TodoStore
) {}
ngOnInit(): void {
this.todo = new Todo;
}
public onSubmit(): void {
this.todoStore.add(this.todo);
this.todo = new Todo;
}
}
一覧のコンポーネント: todos.body.ts
todos.body.ts
はtodos.detail.ts
を利用していましたのでproviders
で宣言していましたがこれが無くなります。
before
@Component({
selector: 'todos-body',
template: `
<todos-detail
*ngFor="let todo of todos; let i = index"
[list-no]="i"
[todo-data]="todo"
(on-delete)="onDelete(i)">
</todos-detail>
`,
directives: [TodosDetailComponent]
})
after
import {Component, OnInit} from '@angular/core';
import {TodoStore} from './shared/todo.store';
import {Todo} from './shared/todo';
@Component({
selector: 'todos-body',
template: `
<todos-detail
*ngFor="let todo of todos; let i = index"
[list-no]="i"
[todo-data]="todo"
(on-delete)="onDelete(i)">
</todos-detail>
`
})
export class TodosBodyComponent
implements OnInit {
private todos: Todo[];
constructor (
private todoStore: TodoStore
) {}
public ngOnInit () {
this.todos = this.todoStore.list;
}
public onDelete(index: number): void {
this.todoStore.delete(index);
}
}
詳細機能用のコンポーネント: todos.detail.ts
todos.detail.ts
は変化なく次の通りです
import {Component, Input, Output, EventEmitter} from '@angular/core';
import {Todo} from './shared/todo';
@Component({
selector: 'todos-detail',
template: `
<div ngClass="list-no">{{listNo+1}}</div>
<div>
<p>{{todo.title}}</p>
<pre>{{todo.desc}}</pre>
<button (click)="onClick($event)">削除</button>
</div>
`,
styles: [`
:host {
display: block;
border:#4e5d5f solid 2px;
margin: 5px 0 5px 0;
padding: 5px 0 5px 0;
width: 100%;
min-height: 112px;
overflow : hidden;
}
.list-no {
text-align: center;
font-size: 2rem;
margin: 5px 5px 5px 5px;
width: 100px;
height: 100px;
background-color: #4e5d5f;
color: #ffffff;
}
div {
float: left;
}
button {
background-color: #e8345a;
}
`]
})
export class TodosDetailComponent {
@Input('list-no')
private listNo: number;
@Input('todo-data')
private todo: Todo;
@Output('on-delete')
private onDelete = new EventEmitter();
public onClick($event: any): void {
this.onDelete.emit($event);
}
}
肝心のNgModule
NgModuleは次のようになります。モジュールで利用するコンポーネントを列記し、その中でもベースとなるエントリーコンポーネント、ブートストラップするコンポーネントを定義します。ブートストラップする方法が以前とは異なります。
todos.module.ts
import {NgModule, ApplicationRef} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {CommonModule} from '@angular/common';
import {FormsModule} from '@angular/forms';
import {HttpModule} from '@angular/http';
import {TodosComponent} from './todos';
import {TodosInputComponent} from './todos.input';
import {TodosBodyComponent} from './todos.body';
import {TodosDetailComponent} from './todos.detail';
@NgModule({
imports: [BrowserModule, CommonModule, FormsModule, HttpModule],
declarations: [TodosComponent, TodosInputComponent, TodosBodyComponent, TodosDetailComponent],
entryComponents: [TodosComponent],
bootstrap: [TodosComponent]
})
export class AppModule {
constructor(appRef: ApplicationRef) {
appRef.bootstrap(TodosComponent);
}
}
ブートストラップは少し変わって次のようになるようです。
main.ts
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {AppModule} from '../components';
platformBrowserDynamic().bootstrapModule(AppModule);
最後に
ざっくりNgModuleを利用してみました。調べるともう少し違う書き方になるかも知れませんが雰囲気は味わえるかなと思います。今回作ったコードはこちらです:
GitHub - albatrosary/Angular2Todos
尚、今回はNightlyを使ってコードを書いています。Nightlyのインストール方法は下記を御覧ください:
How to Use Angular 2 Nightly Builds | <output type="laco">