JavaScriptフレームワーク「Angular」による掲示板システム構築

こんにちは。サイオステクノロジー技術部 武井です。

今回は、Java Scriptフレームワーク「Angular」の紹介と、Angularで簡単な掲示板システムを作成したいと思います。

Angularはクライアントサイドフレームワークです。その名の通り、クライアントを作成することに特化したものです。従来のWebシステムでは、サーバー側でHTMLを生成し、そのHTMLをブラウザが受け取って、画面を表示していました。

しかしながら、スマートフォンなどのマルチデバイス対応が求められる昨今、サーバーはRestfulなインターフェースでJOSNのみを返し、そのJSONによって、クライアント側で画面を生成するのが主流です。Agularもそんなフレームワークです。基本的には、ビューはクライアント側で持ち、サーバーから受け取ったJSONに基づき、クライアント側でHTMLを生成します。

Angularの記述言語はType Scriptです。Type Scriptで記述して、node.jsによってJava Scriptにコンパイル・ビルドします。なので、開発にはnode.jsは必須となります。

まずは、開発環境の構築から、ご説明致します。

開発環境の構築

まず、node.jsのバージョンを管理するためのツールnodebrewをインストールします。

$ curl -L git.o/nodebrew | perl - setup
$ echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.bash_profile

次に、node.jsをインストールします。

$ nodebrew install-binary stable

こうすると、最新の安定バージョンがインストールできます。インストールされたバージョンを確認してみます。

$ nodebrew ls
v9.2.1

今は9.2.1がstableのようです。では、このバージョンのnode.jsを使いたいので、以下のように入力します。

$ nodebrew use v9.2.1

これでバージョン9.2.1が使えるようになりました。

以上で、開発環境の構築は完了です。

掲示板システムの構築

では、Angularで本格的なアプリを作ってみたいと思います。CRUD(Create、Read、Update、Delete)を試すことができる簡単な掲示板です。私は、新しいフレームワークを試すときは、必ずこのアプリを作ります。本アプリケーションのソースは以下のGitHubで公開しています。

https://github.com/noriyukitakei/BBSRestAPI

アプリケーションの画面は以下のとおりです。

まずログイン画面です。

Screen Shot 2017-12-20 at 22.56.57

ログインすると、メッセージの一覧画面が表示されます。新規登録ボタンをクリックすると、新規にメッセージを登録する画面に遷移します。タイトルをクリックするとメッセージ変更画面に遷移し、削除をクリックするとメッセージが削除されます。

Screen Shot 2017-12-20 at 23.00.01

メッセージの新規登録画面です。

Screen Shot 2017-12-20 at 23.00.14

メッセージの変更画面です。

Screen Shot 2017-12-20 at 23.00.25

Angularのソースコードを説明する前に、本アプリケーションが利用するRest APIのインターフェースの説明をします。先程もご説明したように、AngularはサーバーサイドからJSONを受け取り、そのJSONによってクライアントサイドにて画面を生成します。なので、JSONを返却するRest APIが必須になります。本アプリケーションでは、以下の仕様のRest APIがあることを前提としています。ちなみにこのRest APIのソースは以下のgitHubにて公開しております。

https://github.com/noriyukitakei/BBSRestAPI

■ 認証API
認証のAPIです。ログインIDとパスワードを送ると、アクセストークンとその有効期限が返ります。以降のAPIでは、HTTPリクエストのAuthorizationヘッダにアクセストークンを付与して下さい。

–<リクエストURL>–
http://[Rest APIサーバーのホスト名]/login

–<Method>–
Post

–<リクエストボディ>–
下記の形式のJSONになります。

{
id:[ログインID]
password:[パスワード]
}

–<レスポンスボディ>–
下記の形式のJSONになります。

{
access_token:[アクセストークン]
expires_in:[アクセストークンの有効期限(秒)]
}

■ メッセージ一覧API
メッセージの一覧を取得するAPIです。

–<リクエストURL>–
http://[Rest APIサーバーのホスト名]/messages

–<Method>–
Get

–<リクエストヘッダ>–
Authorization: Bearer [認証APIで取得したアクセストークン]

–<リクエストボディ>–
なし

–<レスポンスボディ>–
下記の形式のJSONになります。

[
{
id:[メッセージID]
title:[タイトル]
author:[投稿者]
comment:[コメント]
},
...
]

■ メッセージ取得API
メッセージの一覧を取得するAPIです。

–<リクエストURL>–
http://[Rest APIサーバーのホスト名]/masseges/[メッセージID]

–<Method>–
Get

–<リクエストヘッダ>–
Authorization: Bearer [認証APIで取得したアクセストークン]

–<リクエストボディ>–
なし

–<レスポンスボディ>–
下記の形式のJSONになります。

{
title:[タイトル]
author:[投稿者]
comment:[コメント]
}

■ メッセージ投稿API
メッセージを投稿するAPIです。

–<リクエストURL>–
http://[Rest APIサーバーのホスト名]/messages/new

–<Method>–
Post

–<リクエストヘッダ>–
Authorization: Bearer [認証APIで取得したアクセストークン]

–<リクエストボディ>–
下記の形式のJSONになります。

{
title:[タイトル]
author:[投稿者]
comment:[コメント]
}

–<レスポンスボディ>–
なし

■ メッセージ変更API
メッセージを変更するAPIです。

–<リクエストURL>–
http://[Rest APIサーバーのホスト名]/messages/update

–<Method>–
Post

–<リクエストヘッダ>–
Authorization: Bearer [認証APIで取得したアクセストークン]

–<リクエストボディ>–
下記の形式のJSONになります。

{
id:[メッセージID]
title:[タイトル]
author:[投稿者]
comment:[コメント]
}

–<レスポンスボディ>–
なし
■ メッセージ削除API
メッセージを削除するAPIです。

–<リクエストURL>–
http://[Rest APIサーバーのホスト名]/messages/[メッセージID]

–<Method>–
Delete

–<リクエストヘッダ>–
Authorization: Bearer [認証APIで取得したアクセストークン]

–<リクエストボディ>–
なし

–<レスポンスボディ>–
なし

アプリケーションのディレクトリ構成は以下のようになっております。

Screen Shot 2017-12-21 at 9.38.35

では、それぞれのソースについて説明いたします。

■ app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule }   from '@angular/forms';
import { HttpModule } from '@angular/http' // (1)
import { MY_ROUTES } from './app.routing'; // (2)
import { AppComponent } from './app.component';
import { LoginComponent } from './component/login/login.component'; // (3)
import { ListComponent } from './component/list/list.component';
import { AddComponent } from './component/add/add.component';
import { UpdateComponent } from './component/update/update.component';
import { MessageService }  from './service/message.service'; // (4)
import { AuthService } from './service/auth.service';
@NgModule({
declarations: [ // (5)
LoginComponent,
AppComponent,
ListComponent,
AddComponent,
UpdateComponent,
],
imports: [
BrowserModule, // (6)
FormsModule,
JsonpModule,
HttpModule,
MY_ROUTES
],
providers:    [ MessageService, AuthService ], // (7)
bootstrap: [AppComponent] // (8)
})
export class AppModule { }

一番最初の呼ばれるルートモジュールと呼ばれるものです。Angularの全てはここから始まります。このモジュールでは、他のところで使うモジュール、コンポーネント、サービス、ルーター等を定義します。主要なもののみピックアップしてご説明します。

(1)では、HTTPクライアントを利用するためのモジュールをインポートしています。Rest APIサーバーからJSONを取得するのに使います。

(2)では、ルーティングの定義ファイルをインポートしています。ルーティングとは、URLのパスと、それに対応して呼ばれるコンポーネントの組み合わせです。

(3)では、各種コンポーネントをインポートしています。メッセージの追加、削除、更新、一覧取得、認証などの画面を表示するためのコンポーネントになります。

(4)では、各種サービスを定義しています。Angularでもアプリケーションに必要な処理はサービスに分離する手法を取ります。

(5)では、アプリケーションの中で使うコンポーネントを宣言しています。ここで宣言しないと、コンポーネントは使えません。

(6)では、アプリケーションの中で使うモジュールをインポートしています。ここで宣言しないと、モジュールは使えません。

(7)では、アプリケーションの中で使うサービスを定義しています。ここで宣言しないと、サービスは使えません。

(8)では、一番最初に起動するコンポーネントを指定しています。

■ app.component.ts

import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
}

ルートコンポーネントです。一番最初に呼ばれます。特にこのコンポーネントの中では何もしていません。ただ、templateUrlで定義しているapp.component.htmlを呼び出しているだけです。

■ app.component.html

<div style="text-align: center;">
<h1>掲示板</h1>
</div>

ルートコンポーネントから呼ばれるHTMLになります。特筆すべきは(1)ですが、これは、後に説明するHTML内のrouterLinkの属性で定義したパスが表示されるエリアになります。詳細は後述します。

■ app.routing.ts

import { ModuleWithProviders }   from '@angular/core';
import { Routes, RouterModule }   from '@angular/router';
import { LoginComponent }  from './component/login/login.component'; // (1)
import { ListComponent }  from './component/list/list.component';
import { AddComponent }  from './component/add/add.component';
import { UpdateComponent }  from './component/update/update.component';
import { ErrorComponent }  from './component/error/error.component';
const myRoutes = [ // (2)
{ path: '', component: ListComponent }, // (3)
{ path: 'messages/:id', component: UpdateComponent }, // (4)
{ path: 'messages/delete/:id', component: ListComponent },
{ path: 'add', component: AddComponent },
{ path: 'login', component: LoginComponent },
{ path: 'error', component: ErrorComponent }
];
export const MY_ROUTES: ModuleWithProviders = RouterModule.forRoot(myRoutes);

(1)で必要なコンポーネントをインポートしています。

(2)でURLのパスと、それに対応して呼ばれるコンポーネントを定義しています。

例えば(3)では、http://[Webサーバーのホスト名]/にアクセスした際に呼ばれるコンポーネントを定義しています。

(4)では、http://[Webサーバーのホスト名]/messages/1にアクセスした際に呼ばれるコンポーネントを定義しています。:idの部分に入力した文字列は、コンポーネントに引き渡されます。後ほど後述します。

■ auth.service.ts

import { Injectable } from '@angular/core'; // (1)
import { Http,Headers } from '@angular/http'; // (2)
import { AuthInfo } from '../dto/auth.info'; // (3)
import { Observable } from 'rxjs/Observable'; // (4)
import 'rxjs/add/operator/map'; // (5)
@Injectable() // (6)
export class AuthService {
constructor(private http: Http) { } // (7)
login(id: string,password: string): Observable { // (8)
const header = new Headers({'Content-Type': 'application/json'}); // (9)
let user = { // (10)
id: id,
password: password
}
return this.http.post('http://localhost:8080/auth',user,{headers:header}) // (11)
.map(
response => {
let authInfo: AuthInfo = new AuthInfo;
let json = response.json(); // (12)
authInfo.access_token = json['access_token'];
authInfo.expires_in = json['expires_in'];
return authInfo;
}
);
}
}

認証処理を行うサービスです。Angularでもソースコードの保守性を高めるために、よく使われる処理をサービスに分離します。

(1)は、サービスの依存性注入に必要なアノテーションをインポートしています。

(2)は、HTTP通信に必要なモジュールです。

(3)は、認証情報を格納するDTOをインポートしています。

(4)は、非同期通信に必要なObservableモジュールをインポートしています。

(5)も(4)と同様です。

(6)は、Injectableアノテーションです。このアノテーションをつけたサービスはAngularが管理するDIコンテナの中に入り、後から依存性注入できるようになります。

(7)は、(2)でインポートしたHTTP通信に必要なサービスを依存性注入しています。

(8)はメソッドの定義です。ユーザーIDとパスワードが引数になっています。戻り値はObservableです。HTTPによる非同期通信を行う際にはObservableは必須になります。

(9)は、リクエストに付与するHTTPヘッダを定義しています。

(10)は、認証APIのリクエストボディにセットするJSONを定義しています。

(11)は、HTTPモジュールによってHTTP通信をしています。postメソッドの第1引数にRest APIのURL、第2引数にリクエストボディ、第3引数にリクエストヘッダをセットします。その後にメソッドチェーンでmapメソッドをつけます。mapメソッドの引数はファンクションになります。responseにはHTTPレスポンスがセットされ、そのHTTPレスポンスをどのように処理するかをアロー関数で定義しています。

(12)は、jsonメソッドでJSON本体を取得しています。認証情報を格納するDTOにセットして、戻り値として返却しています。

■ message.service.ts

import { Injectable } from '@angular/core';
import { Message } from '../dto/message';
import { Http,Headers } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
@Injectable()
export class MessageService {
constructor(private http: Http) { }
getMessage(id: string):Observable {
let accessToken = localStorage.getItem('access_token'); // (1)
const header = new Headers({'Authorization': 'Bearer ' + accessToken}); // (2)
return this.http.get('http://localhost:8080/messages/' + id,{headers:header}).map (  // (3)
response => {
let message: Message = new Message;
let json = response.json();
message.id = json['id'];
message.author = json['author'];
message.title = json['title'];
message.comment = json['comment'];
return message;
}
).catch(e => { // (4)
if (e.status === 401) {
return Observable.throw('Unauthorized'); // (5)
}
})
}
・・・

投稿されたメッセージを操作するサービスになります。ここでご説明するのは、特定のメッセージIDのメッセージを取得するメソッドです。

(1)は、ローカルストレージ(HTML5の機能でブラウザに情報を格納するストレージのようなもので、平たく言うとCookieの進化版)に格納された認証情報を取り出しています。認証処理のところで、認証APIのレスポンスであるアクセストークンをローカルストレージに格納します(後述)。ここでは、そのアクセストークンを取り出しています。

(2) (1)で取得したアクセストークンをHTTPヘッダに付与するために、HTTPモジュールのHeadersオブジェクトを生成してます。つまり、APIを呼び出すときにAuthorizationのHTTPヘッダーにアクセストークンを付与しています。Rest API側では、このアクセストークンを検証して、問題なければレスポンスを返し、そうでなければHTTPステータスコード401を返します。

(3)は、APIを呼び出しています。Getメソッドため、getを呼び出しており、第1引数にAPIのURL、第2引数に(2)で生成したヘッダー情報をセットします。後は先程説明した認証APIのところと処理は同じです。

(4)はエラー処理です。おそらくHTTPステータスコード200以外が返されたときに、catch内に遷移するものと思われます。ここでは、Rest APIが401を返したときに、呼び出し元に例外をthrowしています。

addMessage(message: Message): Observable{
let accessToken = localStorage.getItem('access_token');
const headers = new Headers({'Authorization': 'Bearer ' + accessToken});
headers.append('Content-Type', 'application/json');
let json = {
title: message.title,
author: message.author,
comment: message.comment
}
return this.http.post('http://localhost:8080/messages/new',json,{ // (1)
headers: headers
}).map (
response => {}
).catch(e => {
if (e.status === 401) {
return Observable.throw('Unauthorized');
}
})
}

message.service.tsの続きです。メッセージを投稿するサービスになります。特筆すべきところはありません。

(1)で、postメソッドで、メッセージを投稿するAPIを呼び出しています。後は、他と同じです。

deleteMessage(id: String): Observable  {
let accessToken = localStorage.getItem('access_token');
const headers = new Headers({'Authorization': 'Bearer ' + accessToken});
headers.append('Content-Type', 'application/json');
return this.http.delete('http://localhost:8080/messages/' + id,{ // (1)
headers: headers
}).map (
response => {}
).catch(e => {
if (e.status === 401) {
return Observable.throw('Unauthorized');
}
})
}

message.service.tsの続きです。メッセージを削除するサービスになります。

他と違うのは、メッセージを削除するAPIはdeleteメソッドなので、(1)でdeleteを呼び出しています。後の処理は他と同じですね。

他にもメッセージを更新するサービスと、メッセージの一覧を取得するサービスがありますが、ほとんど構成は同じなので、説明は割愛します。

■ login.component.ts

import { Component, Injector } from '@angular/core';
import { AuthService } from '../../service/auth.service';
import { AuthInfo } from '../../dto/auth.info';
import { Router } from '@angular/router'; // (1)
@Component({
selector: "my-app",
templateUrl: './login.component.html',
})
export class LoginComponent {
constructor(private injector: Injector,private router:Router) {} // (2)
user ={
id: '',
password: ''
}
login() {
let authService = this.injector.get(AuthService); // (3)
authService.login(this.user.id,this.user.password).subscribe( // (4)
response => {
localStorage.setItem("access_token",response.access_token); // (5)
this.router.navigate(["/"]); // (6)
},
error => {}
)
}
}

ログイン画面を表示するためのコンポーネントになります。

(1)は、Routerモジュールをインポートしています。これは、ログインした後に、メッセージ一覧表示画面に遷移したいので、Routerモジュールが必要になるためです。

(2)は、依存性注入を行うためのInjector、ルーティングを行うためのRouterを定義しています。

(3)は、先程説明した認証を行うサービスを依存性注入しています。

(4)は認証を行うサービスのloginメソッドを呼び出しています。引数にはユーザーIDとパスワードを指定しています。メソッドチェーンでsubscribeメソッドをつなげて、第1引数に成功時の処理、第2引数にエラー時の処理を定義します。

(5)は成功時の処理です。responseを引数にして、アロー関数で処理内容を定義しています。responseにはHTTPレスポンスがセットされます。ここでは、レスポンスは認証サービス(auth.service.tsのloginメソッドの戻り値である認証情報)が返却するアクエストークンを含むJSONなので、それを解析して、ローカルストレージに格納しています。メッセージを表示したり追加したりするサービスでAuthorizationヘッダにセットしていたアクセストークンは、ここでローカルストレージに格納していたのです。

(6)ではメッセージ一覧画面に遷移しています。Routerモジュールで実現しています。

■ login.component.html

<form novalidate="">
<div><label for="id">ID:</label> <input id="id" name="id" required="" type="text" /> IDは必須です。</div>
<div><label for="password">パスワード:</label> <input id="password" name="password" required="" type="text" /> パスワードは必須です。</div>
<div><input disabled="disabled" type="submit" value="送信" /> <input disabled="disabled" type="reset" value="リセット" /></div>
</form>

ログイン画面のHTMLになります。先程のlogin.component.tsから呼び出されます。

■ list.component.ts

import { Component, Injector } from '@angular/core';
import { Message } from '../../dto/message';
import { MessageService } from '../../service/message.service';
import { Router } from '@angular/router';
import 'rxjs/add/observable/throw'; // (1)
@Component({
selector: 'app-root',
templateUrl: './list.component.html'
})
export class ListComponent {
messages :Message[];
constructor(private injector: Injector,private router:Router) {}
ngOnInit() { // (2)
let messageService = this.injector.get(MessageService);
messageService.getMessages().subscribe( // (3)
response => {
this.messages = response;
},
error => { // (4)
if (error === 'Unauthorized') {
this.router.navigate(["/login"]);
}
}
);
}

メッセージ一覧画面を表示するコンポーネントです。

(1)は、後述する(4)でエラー処理を行うために必要なインポート処理です。

(2)は、コンポーネント起動時に一番最初に呼ばれる処理を定義しています。ここでメッセージの一覧を定義しています。

(3)は、メッセージを取得するサービスから、メッセージの一覧を取得するメソッドを呼び出しています。成功時はインスタンス変数で定義したmessagesにレスポンスを入れています。

(4)は、サービス側で発生した例外をキャッチする処理です。先述しましたようにサービス側では、Rest APIのHTTPステータスコードが401が返却されたときに例外をthrowしています。それをキャッチする処理です。Routerモジュールによりログイン画面に遷移しています。

delete(id: string) {
let messageService = this.injector.get(MessageService);
messageService.deleteMessage(id).subscribe( // (1)
response => {
location.reload(); // (2)
},
error => {
if (error === 'Unauthorized') {
this.router.navigate(["/login"]);
}
}
);
}

list.component.tsの続きです。メッセージを削除するメソッドです。

(1)は、メッセージを処理サービスの、メッセージを削除するメソッドを呼び出しています。

(2)は、成功時の処理です。もう一度一覧画面を表示するためにページ全体をリロードしています。削除したメッセージを一覧画面に反映するためです。

■ list.component.html

<a routerLink="/add">新規登録</a> // (1)
<table border="1">
<tr>
<td>No</td>
<td>タイトル</td>
<td>投稿者</td>
<td>コメント</td>
<td>削除</td>
</tr>
<tr *ngFor="let message of messages"> // (2)
<td>{{message.id}}</td>
<td><a href="/messages/{{message.id}}">{{message.title}}</a></td> // (3)
<td>{{message.author}}</td>
<td>{{message.comment}}</td>
<td><a href="#" (click)="delete(message.id)">削除</a></td> // (4)
</tr>
</table>
<router-outlet></router-outlet>

メッセージを一覧画面を表示するHTMLです。

(1)は、メッセージの新規登録画面に遷移するリンクです。routerLinkに、Routerコンポーネントで定義したパスを入れることにより、それに対応したコンポーネントが呼ばれる仕組みです。

(2)は、コンポーネントからデータバインディングされたメッセージの一覧情報をngForでループ処理しています。

(3)は、メッセージ変更画面へのリンクです。

(4)は、メッセージを削除するためのリンクです。(link)のイベントプロパティにより、list.component.tsで定義したdeleteメソッドが呼ばれます。引数には、メッセージIDを渡しています。このリンクをクリックすると、メッセージは削除されます。

■ add.component.ts

import { Component, Injector } from '@angular/core';
import { MessageService } from '../../service/message.service';
import { Message } from '../../dto/message';
import { Router } from '@angular/router';
@Component({
selector: "my-app",
templateUrl: './add.component.html',
})
export class AddComponent {
constructor(private injector: Injector,private router:Router) {}
message = {
title: '',
author: '',
comment: '',
};
add() {
let message = new Message;
message.author = this.message.author; // (1)
message.title = this.message.title;
message.comment = this.message.comment;
let messageService = this.injector.get(MessageService);
messageService.addMessage(message).subscribe( // (2)
response => {
this.router.navigate(["/"]); // (3)
},
error => {}
);
}
}

メッセージを新規登録するためのコンポーネントです。

(1)には、新規登録画面で入力フォームで入力された値が、双方向データバインディングによってセットされます。

(2)は、メッセージを操作するサービスの、メッセージ追加するメソッドをを呼び出しています。

(3) 処理成功時には、メッセージ一覧画面に遷移します。

■ add.component.html

<form novalidate="">
<div><label for="title">タイトル:</label> <input id="title" title="" name="title" required="" type="text" /> タイトルは必須です。</div>
<div><label for="author">投稿者:</label> <input id="author" name="author" required="" type="text" /> 投稿者は必須です。</div>
<div><label for="comment">コメント:</label> <input id="comment" name="comment" required="" type="text" /> コメントは必須です。</div>
<div><input disabled="disabled" type="submit" value="送信" /> <input disabled="disabled" type="reset" value="リセット" /></div>
</form>

メッセージを登録画面のHTMLです。Angularの構文に従った一般的なHTMLなので、特筆すべきところはありません。

■ update.component.ts

import { Component, Injector } from '@angular/core';
import { MessageService } from '../../service/message.service';
import { Message } from '../../dto/message';
import { ActivatedRoute } from '@angular/router';
import { Router } from '@angular/router';
@Component({
selector: "my-app",
templateUrl: './update.component.html',
})
export class UpdateComponent {
constructor(private injector: Injector,private activatedRoute:ActivatedRoute,private router:Router) {}
id = this.activatedRoute.snapshot.paramMap.get('id'); // (1)
message = {
id: this.id,
title: '',
author: '',
comment: '',
};
ngOnInit() { // (2)
let messageService = this.injector.get(MessageService);
messageService.getMessage(this.id).subscribe( // (3)
response => {
this.message = response; // (4)
},
error => {}
);
}
update() {
let messageService = this.injector.get(MessageService);
messageService.updateMessage(this.message).subscribe( // (5)
response => {
this.router.navigate(["/"]); // (6)
},
error => {}
)
}
}

メッセージを更新するコンポーネントになります。

(1)はメッセージ更新画面を表示する際にURLで指定されたメッセージID(http://[Webサーバーのホスト名]/messages/1)の1の部分を取得するための処理になります。

(2)は、コンポーネントが呼び出されるときに一番最初に行われる処理で、更新画面のフォームに設定する初期値を取得する処理をしています。

(3)は、指定したメッセージIDのメッセージ情報を取得するメソッドを呼び出しています。

(4)は、成功時の処理で、メッセージのインスタンス変数にレスポンス情報を格納しています。

(5)は、メッセージ更新画面で、更新ボタンが押下されたときの処理になります。メッセージを処理するサービスの、メッセージを更新するメソッドを呼び出しております。

(6)は、成功時にメッセージ一覧画面に表示する処理です。

■ update.component.html

<a routerLink="/">一覧に戻る</a>
<form #myForm="ngForm" (ngSubmit)="update()" novalidate>
<div>
<label for="title">タイトル:</label>
<input id="title" name="title" &#91;(ngModel)&#93;="message.title" #title="ngModel" required />
<span *ngIf="title.errors?.required && title.dirty">タイトルは必須です。</span>    
</div>
<div>
<label for="author">投稿者:</label>
<input id="author" name="author" &#91;(ngModel)&#93;="message.author" #author="ngModel" required />
<span *ngIf="author.errors?.required && author.dirty">投稿者は必須です。</span>    
</div>
<div>
<label for="comment">コメント:</label>
<input id="comment" name="comment" &#91;(ngModel)&#93;="message.comment" #comment="ngModel" required />
<span *ngIf="comment.errors?.required && comment.dirty">コメントは必須です。</span>    
</div>
<div>
<input type="submit" value="送信" &#91;disabled&#93;="myForm.invalid" />
<input type="reset" value="リセット" &#91;disabled&#93;="myForm.pristine" />
</div>
</form>

メッセージを更新する画面のHTMLです。新規登録画面とほぼ変わらず特筆するべきことろはありません。

以上、Angularで構築した簡単な掲示板システムのご説明でした。今後はこのようなクライアントサイドフレームワークを用いた開発が主流になってくると思いますので、ぜひこれを機にAngularを触ってみて下さい。

Be the first to comment

コメント投稿

Your email address will not be published.


*