はじめに
本ブログはAngulerを触ったことがない筆者が書籍1冊でどこまで理解できるか試してみた内容です。
下記の書籍を読んでいます。
Angular アプリケーションプログラミング:書籍案内|技術評論社
最初の章 イントロダクション
Javascriptの歴史からなぜ復権したかの記述がありました。
フレームワークの候補がVue.js, React.jsとあり、なぜAngulerに本書籍では言及しているのかの記述もありました。
2章 Angulerの基本
- node.jsのインストール
- 必要なモジュールインストール
- Anguler quick startをダウンロード
- アプリを起動
nodeを入れる理由はモジュール管理がnpmで可能、サーバーサイドの準備が簡単などの理由からだと思います。
4行程度のコードで下記の画面まで出せます。
git clone https://github.com/angular/quickstart
cd quickstart
npm install
npm start
サンプルアプリからモジュールとコンポーネントの概念を学びます。
- モジュール:Angulerアプリを構成する要素
- コンポーネント:UI部品が主でモジュールの下位の概念
起動時に必要なモジュールはルートモジュールと呼ばれます。
書籍では命名規則に関しても言及してくれています。
コンポーネント
UIの表示の方法:template
UIのアクション:selector
起動スクリプト
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
必要なモジュールを導入してブラウザーを起動しています。
メインページ
<!DOCTYPE html>
<html>
<head>
<title>Angular QuickStart</title>
<base href="/">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- Polyfill(s) for older browsers -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('main.js').catch(function(err){ console.error(err); });
</script>
</head>
<body>
<my-app>Loading AppComponent content here ...</my-app>
</body>
</html>
処理の流れは
- 必要なモジュールをインポート
- システム情報をインポート
- main.jsを起動
-
<body>
部分で表示
これらの流れを書籍では丁寧に解説してくれています。
フロントエンドの開発に必須のデベロッパーツールの紹介もされています。
設定ファイル
- package.json: インストールモジュールの設定
- tsconfig.json: タイプスクリプトの型厳しさの設定
- systemjs.config.js: Javascriptモジュールの動的ロードの設定
3章 データバインディング
データバインディングに関してです。
コンポーネントの値をテンプレートに移したり、テンプレートの変化をコンポーネントに移したりしています。
Interpolation構文
@Component({
selector: 'my-app',
template: `<h1>Hello {{name}}</h1>`,
})
{{name}}
で値を補完しています。
書籍では安全にアクセスする方法、使えないパターン、ガイドラインについても言及がありました。
プロパティバインディング
下記のようなパターンをプロパティバインディングと呼ぶようです。
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: '<img [src]="image">',
})
export class AppComponent {
image = 'http://www.wings.msn.to/image/wings.jpg'
}
[innerHTML]
を用いたHTMLの埋め込み方法についても言及していました。
iframeの表示にはbypassSecurityTrustHtml
を使用すれば表示可能ですがセキュリティホールの原因にもつながるので使用時はリソースのチェックが必要と記述されていました。
表などで必要な属性バインディングにも言及されていました。
スタイルの反映が可能なクラスバインディング
下記のようにスタイルを決定しておくとスタイルを有効化無効化も設定できるのがクラスバインディングです。
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `<div class='line back' [class.fore]='flag'>Wings</div>`,
styles: [`
.line {boder: solid 1px #f00; }
.back {background-color: #0ff; }
.fore {color: Red; }
`]
})
export class AppComponent {
flag = true;
}
スタイルバインディングでも同様の動作が可能です。書籍では具体的な例も載っていました。
スタイルバインディングはコードの中にCSSスタイルの情報が混在するために非推奨でした。
イベントバインディング
ユーザーがクリックなどの動作を行った時の情報をどのようにコンポ−ネントに渡すかの仕組みです。
下記のコードでボタンを押した時の動作を確認できます。
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `<input type='button' (click)="show($event)" value="現在時刻"/>
{{msg}}
`
})
export class AppComponent {
msg = '---';
show(e: any) {
this.msg = new Date().toLocaleString();
console.log(e)
}
}
結果として下記のような画面になります。
書籍ではマウスの座標の取得、キー情報の取得、イベントのデフォルト動作のキャンセルなどの記述もありました。
テンプレート参照変数による入力値の取得
下記のコードでテキストボックスに入力された値をエンターを押した際に表示します。
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `
<input #txt id='txt' name='txt'
type='text' (keyup.enter)='show(txt.value)'
/>
<ul [innerHTML]='msg'></ul>
`,
styleUrls: ['app/app.component.css']
})
export class AppComponent {
msg = '';
show(input: string){
this.msg += `<li>${input}</li>`;
}
}
#
が付いている要素をコンポーネントに渡すことが可能になっています。これでイベント情報から情報をたどって特定の値を表示せずに表示させることがかのうです。
keyup.enter
によってエンターが押されたときだけ動作するように制限がかかっています。
双方向バインディング
ビューとコンポーネントの両者で値を同期させる方法です。動的に記述が変化するので面白いです。
入力値を特定の値に変更する(ngModelChange)='myName=$event.toUpperCase()'
の説明もあります。
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `
<form>
<label for='name'>Name: </label>
<input id='name' name='name' type='text'
[ngModel]='myName'
(ngModelChange)='myName=$event.toUpperCase()'
/>
<div> Hello {{myName}}</div>
</form>
`,
})
export class AppComponent {
myName = 'Hoge'
}
4章 パイプ/ディレクティブ
パイプ
パイプについての紹介です。テンプレートに埋め込まれたデータを加工、整形するための仕組みです。
下記の用にtemplate内に記述すればtitle
変数に対してパイプ処理を行います。下記のケースでは大文字に変換する処理です。
<p>uppercase: {{ title | uppercase}}</p>
他にもjson形式で変更可能であったり、必要な部分文字列を抜き出したり、通貨形式にしたり、%形式などが紹介されていました。
ディレクティブ
独自のHTMLの要素をそう呼ぶ
ngIf
: 特定の条件のみ出力する。
ngSwitch
: 値に応じて表示するものを変更
ngFor
: 下記のように記述して、特定の項目の表示を繰り返す
ビュー部分
<tr *ngFor='let b of books'>
<td>{{b.isbn}}</td>
</tr>
コンポーネント部分
books = [
{
isbn: '11111'
},
{
isbn: '2222'
},
];
書籍では効率よく追加削除を追跡するトラッキング式というものが紹介されていました。
-
ngStyle
:スタイルを動的に変更したり、複数のスタイルプロパティを設定可能
@Component({
selector: 'my-app',
template: `
<input type='button' (click)='back=!back' value='背景色'/>
<input type='button' (click)='fore=!fore' value='前景色'/>
<input type='button' (click)='space=!space' value='余白'/>
<div [ngStyle]='styles'>
<p>Writes </p>
</div>`
})
export class AppComponent {
back = false;
fore = false;
space = false;
get styles() {
return {
'background-color': this.back ? '#f00' : '',
'color': this.fore ? '#fff' : '#000',
'padding.px': this.space ? 15 : 5,
};
}
}
下記のようなボタンがでてボタンを押すと下の文字が変更されます。
ngClass
でも同様のことが可能です。
ngTemplateOutlet
:用意されたテンプレ−トの内容をインポートする
ngComponentOutlet
:用意されたテンプレ−トの内容を動的にビューポイントにインポートする
5章 フォーム開発
入力ボックスの用意
<input id='mail' name='mail' type='email' [(ngModel)]='user.mail' required email #main='ngModel'>
name='mail'
: フォーム識別要素のためのキー
[(ngModel)]='user.mail'
: フォーム要素とプロパティのヒモ付
required email
: 検証ルール
#main='ngModel'
: フォーム要素参照のための変数宣言
ラジオボタンの設置
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `
<from #myForm='ngForm'>
<ng-container *ngFor='let item of data; index as i'>
<label>
<input type='radio' name='animal'
[(ngModel)] = 'selected'
[value] = 'item.value' [checked]="selected == item.value"
(change)="show(i)">{{item.label}}
</label><nr />
</ng-container>
</form>
`})
export class AppComponent {
selected = 'hamster';
data = [
{ label: 'dog', value: 'dog_value'},
{ label: 'cat', value: 'cat_value'},
{ label: 'hamster', value: 'hamster_value'},
];
show(i: number) {
console.log('current label ' + this.data[i].label);
console.log('current value ' + this.selected);
}
}
文字数カウント機能付きのテキストエリアを設置
import { Component } from "@angular/core";
@Component({
selector: "my-app",
template: `
<form>
<textarea cols="70" rows="5" name="tweet"
[(ngModel)] = "tweet" (input)="selector()"></textarea>
<div [ngStyle]="myStyle">{{count}}</div>
</form>`
})
export class AppComponent {
max = 140;
tweet = "";
count = this.max;
myStyle = {color: "#00f", fontWeight: 'normal'};
selector() {
this.count = this.max - this.tweet.length;
if (this.count > 10){
this.myStyle = {color: "#00f", fontWeight: 'normal'};
} else if (this.count > 0){
this.myStyle = {color: "#f0f", fontWeight: 'normal'};
} else {
this.myStyle = {color: "#f00", fontWeight: 'bold'};
}
}
}
他にも気切り文字で分割する例やファイルアップロード機能も紹介されていました。
また入力値の検証方法としてモデル駆動型とテンプレート駆動型の比較紹介もされていました。
6章 コンポーネント
この章では複数コンポーネントを用いてアプリを実現する方法とコンポーネント間で情報を渡す方法について言及しています。
親コンポーネントから子コンポーネントへのデータの引き渡しには下記のような@Input
構文が必要です。
@Input() item: Book;
上記でコンポーネントに設定した場合は下記のように他のコンポーネントで定義したitem
の値を渡せます。
<detail-book [item]="selected"></detail-book>
逆に子コンポーネントから親コンポーネントへのデータの引き渡しには下記のような@Output
構文が必要です。
下記の場合だと親コンポーネントから受け取ったオブジェクトを処理をして親コンポーネントに返すために@Output
構文を使用してサブミット処理があった場合にemit
処理をしています。
@Output
の部分はイベント名と型の宣言だけなので実際の処理はemit
によって行われます。
@Input() item :Book;
@Output() edited = new EventEmitter<Book>();
onsubmit() {
this.edited.emit(this.item);
}
書籍ではコンポーネントのライフサイクルにも言及していました。
ライフサイクルとは最初に生成された後、プロパティ子コンポーネントの変化を受けて状態を変化させていき、非表示のタイミングで破棄されます。この一連の流れをライフサイクルとよんでいます。
Angulerでは標準で用意されているライフサイクルメソッドがあります。
ngAfterVuewInit
とngAfterVuewChecked
の具体例が記述されていました。
コンポーネントスタイル:外部のスタイルに影響しない点が利点らしいです。
アニメーション動作の具体的な設定方法にも言及されていました。
他にも開発で重要なブラウザ上での通信の見方やソースのブレイクポイントにも言及されていました。
template
部分に直にHTMLを記述せずに外部からロードする方法についても言及していました。
7章 サービス開発
コンポーネントにすべての記述を今までのサンプルでは行っていましたが本来はビジネスロジック部分は別の部分に移さないとコードの見通しが悪くなります。
そこでそれを明示的に分離するのがサービスです。
下記のように@Injectable
を追記してサービスクラスを作成します。
@Injectable
export class BookService{
getBooks(): Book[]{
}
}
書籍では依存性注入の概念についても説明しています。
DIコンテナーがインスタンスの管理を行うことで依存性の注入を行っています。
下記の場合はconstructor
に使用するサービスを設定している例です。
constructor(private bookservice: BookService)
依存性の注入方法にクラスを使わない例、依存性注入に理解が必要なプロパティの例、複数のプロパティをひもづけるmulti
の紹介、
開発者ツールとしてPretty Print
の紹介がされていました。
XMLHttpRequest
XMLHttpRequestはサーバーとの非同期通信です。
下記の部分がコンポーネントで記述される部分です。
constructor(private http:Http)()
onclick() {
this.http.get('app/http.php', {
params: {name: this.name}
}).subscribe(
response => {
this.result = response.text();
},
error => {
this.result = 'Error: $(error.statusText)';
}
)
}
get
でURLにアクセスし、そのときにオプションで引数も加えれます。subscribe
でresponse
の結果を取得して表示しています。
書籍ではサーバサイドはphpによる紹介ですがサーバーサイドは自分で建てられるのであれば言語は問わないと記述がありました。
非同期処理をサービスに分離するコードも記述されています。上の例ではコンポーネントに記述されています。
別でサービスを記述すれば下記のように呼び出すことが可能です。
import ['サービス名'] from 'サービスが記述されたファイル'
@Component({
:
providers: ['サービス名']
:
})
export class AppComponent {
constructor(private test_service: 'サービス名')
onclick() {
this.test_service.'メソッド名'
}
}
ウェブサーバーの用意が面倒なのでAngulerではメモリ上で動作するanguler-in-memory-web-api
を提供しています。
- 擬似データベースのクラスを準備
- ルートモジュールに
anguler-in-memory-web-api
を通して擬似データベースを登録 - Httpサービスからは通常のデータベースと同様にアクセス
非同期処理のasync
、セキュリティ対策の例としてXSRF対策のためのトークン利用、XSSI対策にも言及していました。
また開発者用ツールとしてDom Break Pointの紹介もありました。
8章 ルーティング 応用編
ページ切り替えもアプリケーションの責務と捉え実装するための方法が記述されています。
ルート用のモジュールを用意して下記のように記述します。
const myRoutes = [
{path: 'exam', component: ExampleComponent},
{path: '', component: MainComponent},
{path: '**', component: ErrorComponent},
];
export const MY_ROUTES: ModuleWithProviders = RouterModule.forRoot(myRoutes);
ルートの設定の順序が重要で**
を最初にするとワイルドカードによってどんなパスでもErrorComponent
に遷移するので注意が必要です。
メインモジュールにルーティングのモジュールと作成したコンポーネントを追加すれば良いと記述がありました。
エラーページを出さないためにトップページにリダイレクトする方法の記載もありました。
ルーター経由で情報を流す手法も紹介していました。
- ルートパラメーター
- ルートパラメーター(可変長)
- クエリ情報・フラグメント
- ルートデータ
- リゾルバ−
またビューに関しても言及しています。
- 1つのページに複数のビュー領域を設置
- ビューを入れ子構造に配置
- ルーティングに際して任意の処理をガード
9章 パイプ・ディレクティブの自作 応用編
この章ではパイプとディレクティブの実装方法と具体的な例が記述されています。
pureなパイプの実装例として改行文字を<br>
に置き換える例などを紹介していました。
pureなパイプとimpureなパイプの紹介がされていました。impureなパイプの方が自由度が高いですがパフォーマンスの劣化にもつながるのでむやみに使用しないほうが良いと記述されています。
10章 テスト
Angulerではユニットテスト、E2Eテストまでカバーしています。
テスティングフレームワーク:Karma
テストランナー:Jasmine
上記の組み合わせが一般的なようです。
またサービス依存している場合のテスト対処方法も記述されています。
E2EテストではProtractorの紹介をしていました。
11章 関連ライブラリ・ツール
書籍では利用できる関連ライブラリの紹介がありました。
気になる方は書籍を手にとってみられると良いと思います。
おまけ
Anguler TipsというAngulerのTipsを解説する連載の紹介がありました。
TSLintの紹介:タイプスクリプトでかくべきコードの紹介がされていました。
感想
個人的に8章までやれば個人開発では十分だとおもいますが9−10章でプロまたはチームとして開発する上で必要だと感じました。
最後に
WINGSプロジェクトの書籍レビュアーに応募し、献本して頂いたので、書評を書きました。
文法を理解するのに体系的にまとめられており、実際に使用できるツールの紹介もあったので良かったです。
本記事では詳細が記述していない部分もありますが書籍では詳細が記述されており、サンプルコードも取得可能です。
Angular アプリケーションプログラミング:書籍案内|技術評論社
WINGSプロジェクト