画面の実装(1)
続いてコントローラーを呼び出すクライアントサイドの実装をしていきます。
ブログ一覧画面
ブログの一覧画面はサイドメニューから遷移して表示される画面とします。そのため初期状態ではすべてのブログを一覧表示し、画面上部に設けた検索フォームから作者名での絞り込み検索ができるようにします。
<h1>ブログ一覧</h1> <!-- 作者名による絞り込み検索用フォーム --> <form> <label for="author">作者:</label> <input id="author" name="author" type="text" #author /> <button (click)="searchByAuthor(author.value)">検索</button> </form> <!-- 検索結果を一覧表示するテーブル --> <table class="table" *ngIf="blogs"> <thead> <tr> <th>ID</th> <th>Blog title</th> <th>Author</th> </tr> </thead> <tbody> <tr *ngFor="let blog of blogs" (click)="searchPostById(blog.blog_id)"> <td>{{ blog.blog_id }}</td> <td>{{ blog.blog_title }}</td> <td>{{ blog.author }}</td> </tr> </tbody> </table>
続いて、この画面に対応するAngularのコンポーネントを実装していきます。
Blogコンポーネント
BlogコンポーネントはBlogコントローラーのメソッドにHttp経由でアクセス(検索を実行)し、取得した検索結果データをブログ一覧画面に渡す役割を持ちます。
import { Component, Inject } from '@angular/core'; import { Http } from '@angular/http'; import { Router } from "@angular/router"; @Component({ selector: 'blog', templateUrl: './blog.component.html' }) export class BlogComponent { // ブログ一覧画面が参照するフィールド public blogs: string[]; // コンストラクタでブログの一覧を取得する ・・・(1) constructor(private http: Http, private router: Router, @Inject('BASE_URL') private baseUrl: string) { // BlogコントローラーのBlogsメソッドにアクセスする this.http.get(this.baseUrl + 'api/blog/blogs').subscribe(result => { this.blogs = result.json() as string[]; }, error => console.error(error)); } // 選択したブログの記事一覧ページへ遷移する ・・・(2) searchPostById(id: string) { this.router.navigate(['/post'], {queryParams: {id: id}}); } // 作者名でブログを検索する ・・・(3) searchByAuthor(author: string) { // BlogコントローラーのFindByAuthorメソッドにアクセスする this.http.get(this.baseUrl + 'api/blog/findByAuthor?author=' + author).subscribe(result => { this.blogs = result.json() as string[]; }, error => console.error(error)); } }
ブログ一覧画面を表示する際、最初に呼び出されるのがコンストラクタです(1)。コンストラクタ内ではHttpコンポーネントを使い、BlogコントローラーのBlogsメソッドを呼び出しています。
Blogsメソッドからレスポンスされたブログの一覧をstring型の配列にキャストし、画面から参照できるフィールド(this.blogs)にセットします。また、ブログ一覧からあるブログを選択した際に記事一覧画面に遷移するためのイベントハンドラーメソッドを用意します(2)。ここではAngularのRouterコンポーネントを使って遷移先画面のパスと、遷移先画面に連携したいデータを渡しています。
searchByAuthorメソッド(3)は、検索フォームの検索ボタンに対応するメソッドです。ユーザーから入力された作者名をURLパラメータに付与してBlogコントローラーのFindByAuthorメソッドを呼び出しています。
記事一覧画面
記事一覧画面は、ブログ一覧画面とほぼ同じ実装になります。
<h1>記事一覧</h1> <!-- 記事のタイトルによる前方一致検索用フォーム --> <form> <label for="title">タイトル(前方一致):</label> <input id="title" name="タイトル" type="text" #title /> <button (click)="searchByTitle(title.value)">検索</button> </form> <!-- 検索結果を一覧表示するテーブル --> <table class="table" *ngIf="posts"> <thead> <tr> <th>ID</th> <th>Post title</th> </tr> </thead> <tbody> <tr *ngFor="let post of posts"> <td>{{ post.post_id }}</td> <td>{{ post.post_title }}</td> </tr> </tbody> </table>
このページに対応対応するコンポーネントも作成します。
Postコンポーネント
Postコンポーネントは、記事一覧画面に対応したコンポーネントで、役割はBlogコンポーネントと同様となります。
import { Component, Inject } from '@angular/core'; import { Http } from '@angular/http'; import { ActivatedRoute } from "@angular/router"; @Component({ selector: 'post', templateUrl: './post.component.html' }) export class PostComponent { public posts: string[]; // コンストラクタでブログに含まれる記事一覧を取得 constructor(private http: Http, private route: ActivatedRoute, @Inject('BASE_URL') private baseUrl: string) { const blogId = this.route.snapshot.queryParamMap.get('id'); ・・・(1) this.http.get(this.baseUrl + 'api/post/posts?blogId=' + blogId).subscribe(result => { this.posts = result.json() as string[]; }, error => console.error(error)); } // タイトル名で記事を検索する searchByTitle(title: string) { this.http.get(this.baseUrl + 'api/post/findByTitle?title=' + title).subscribe(result => { this.posts = result.json() as string[]; }, error => console.error(error)); } }
Postコンポーネントも、コンストラクタでPostコントローラーを呼び出して記事一覧を取得しています。ただし、記事一覧画面はブログ一覧画面で選択したブログの記事のみを表示する必要があるため、BlogコンポーネントのsearchPostByIdメソッドでRouterコンポーネントにセットしたデータを取り出してURLパラメータとして使用しています(1)。
ナビゲーション・Angularモジュールの修正
画面の左側に表示されるナビゲーションメニューに、ブログ一覧画面へのリンクを追加します。
・・・中略 <li [routerLinkActive]="['link-active']"> <a [routerLink]="['/blog']"> <span class='glyphicon glyphicon-th-list'></span> Blogs </a> </li>
Angularのモジュールの設定
クライアント側実装の最後に、ここまでで作成したコンポーネントをモジュールに設定していきます。
・・・中略 import { BlogComponent } from "./components/blog/blog.component"; // 追加 import { PostComponent } from "./components/post/post.component"; // 追加 @NgModule({ declarations: [ ・・・中略 HomeComponent, // カンマを追加 BlogComponent, // 追加 PostComponent // 追加 ], imports: [ ・・・中略 RouterModule.forRoot([ ・・・中略 { path: '**', redirectTo: 'home' }, // カンマを追加 { path: 'blog', component: BlogComponent }, // 追加 { path: 'post', component: PostComponent } // 追加 ]) ] })
Blog・Postコンポーネントをインポートし、declarationsのリストに追加することでコンポーネントの利用宣言をします。またimports内のRouterModule.forRootメソッドのリストにコンポーネントと対応するパスを記述することで、Routerコンポーネントによる画面遷移が可能となります。