| ページ一覧 | ブログ | twitter |  書式 | 書式(表) |

MyMemoWiki

「Angular」の版間の差分

提供: MyMemoWiki
ナビゲーションに移動 検索に移動
 
(同じ利用者による、間の182版が非表示)
1行目: 1行目:
 +
| [[AngularJS]] | [[TypeScript]] | [[Google Cloud Platform]] | [[Bootstrap]] | [[Flask]] | [https://www.typea.info/blog/index.php/category/angular/ ブログカテゴリ(Angular)] |
 +
{{amazon|4774191302}}
 
==[[Angular]]==
 
==[[Angular]]==
[[TypeScript]] | [[Google Cloud Platform]] | [[Bootstrap]] | [[Flask]] |
+
===Angularに関連するブログエントリ====
{{amazon|4774191302}}
+
*[https://www.typea.info/blog/index.php/category/angular/ Angularに関連するブログエントリ]
 +
 
 
==[[Angular]] CLI==
 
==[[Angular]] CLI==
 
===準備===
 
===準備===
13行目: 16行目:
 
*実行
 
*実行
 
  > npm start
 
  > npm start
 +
[[File:angular_quickstart.png]]
 +
====要素====
 +
{|class="wikitable"
 +
|+ルートモジュール
 +
!要素名!!説明
 +
|-
 +
|コンポーネント||UI部品
 +
|-
 +
|サービス||ビジネスロジック
 +
|-
 +
|パイプ||表示値の加工、演算
 +
|-
 +
|ディレクティブ||文書ツリーの操作
 +
|}
 +
 +
*モジュール
 +
**Angularにおけるモジュールの実態は、TypeScriptのクラス
 +
**定義しただけではモジュールとみなされず、@NgModuleデコレータで宣言が必要
 +
 +
====設定ファイル====
 +
{|class="wikitable"
 +
!設定ファイル!!概要
 +
|-
 +
|package.json||利用するライブラリ情報
 +
|-
 +
|tsconfig.json||TypeScriptコンパイラーの動作を
 +
|-
 +
|systemjs.config.js||モジュールローダー(SystemJS)の設定
 +
|}
  
 
===インストール===
 
===インストール===
21行目: 53行目:
 
===アプリケーションの実行===
 
===アプリケーションの実行===
 
  > ng serve
 
  > ng serve
 +
 
===[[Angular]] CLI の主なコマンド===
 
===[[Angular]] CLI の主なコマンド===
 +
----
 
*https://github.com/angular/angular-cli/wiki
 
*https://github.com/angular/angular-cli/wiki
 
{|class="wikitable"
 
{|class="wikitable"
34行目: 68行目:
 
|-
 
|-
 
|ビルドして起動
 
|ビルドして起動
|ng serve
+
|[https://angular.io/cli/serve ng serve]
 
|-
 
|-
 
|ビルド
 
|ビルド
64行目: 98行目:
 
|-
 
|-
 
|}
 
|}
=====ng generate サブコマンド=====
+
=====オブジェクト生成 ng generate サブコマンド=====
 +
----
 
{|class="wikitable"
 
{|class="wikitable"
 
!要素
 
!要素
97行目: 132行目:
 
|-
 
|-
 
|}
 
|}
 +
 +
==ng-bootstrap==
 +
**https://ng-bootstrap.github.io/
 +
 
==ngx-bootstrap==
 
==ngx-bootstrap==
 
*https://valor-software.com/ngx-bootstrap
 
*https://valor-software.com/ngx-bootstrap
121行目: 160行目:
  
 
[[File:0167_bootstrap_alert.jpg]]
 
[[File:0167_bootstrap_alert.jpg]]
 +
 
==[[Angular]] Material==
 
==[[Angular]] Material==
 
*https://material.angular.io/
 
*https://material.angular.io/
 +
 +
<pre>
 +
$ ng add @angular/material
 +
</pre>
 +
 +
===カスタムテーマ===
 +
* https://www.creative-tim.com/product/material-dashboard-angular2
 +
* [https://zenn.dev/fusho_takahashi/articles/20d044c4a2d459b5c2ca Angular Material でカスタムカラーを使う]
 +
* [http://mcg.mbitson.com/#!?mcgpalette0=%233f51b5 カラーパレット生成]
 +
* [https://material.io/design/color/the-color-system.html カラーシステム]
 +
* [https://qiita.com/daikiojm/items/7b77edb49306d29f389d Angular Materialのカスタムテーマを設定]
 +
* [https://github.com/angular/components/blob/c1f6ea19cb317d30af79dc431d39ed8b962e146d/src/lib/core/theming/_palette.scss Angular Material _palette.scss]
 +
 
==非同期通信==
 
==非同期通信==
 
=====app.module.ts=====
 
=====app.module.ts=====
144行目: 197行目:
 
|-
 
|-
 
|コンポーネント -&gt; ビュー
 
|コンポーネント -&gt; ビュー
|補間
+
|Interpolation(補間)
|{{...}}
+
|<pre>{{...}}</pre>
 
|-
 
|-
 
|コンポーネント -&gt; ビュー
 
|コンポーネント -&gt; ビュー
 
|プロパティ/属性バインディング
 
|プロパティ/属性バインディング
|[property]='value'
+
|<pre>[property]='value'</pre>
 
|-
 
|-
 
|ビュー -&gt; コンポーネント
 
|ビュー -&gt; コンポーネント
 
|イベントバインディング
 
|イベントバインディング
|(event)='handler'
+
|<pre>(event)='handler'</pre>
 
|-
 
|-
 
|コンポーネント &lt;-&gt; ビュー
 
|コンポーネント &lt;-&gt; ビュー
 
|双方向バインディング
 
|双方向バインディング
|[(target)]='value'
+
|<pre>[(target)]='value'</pre>
 +
|-
 +
|フォーム要素
 +
|input/textarea/selectなど,
 +
フォーム要素をバインドするには、ngModel を利用する
 +
|<pre><textarea [(ngModel)]="memo"></textarea></pre>
 
|-
 
|-
 
|}
 
|}
 +
 +
 
====テンプレート参照変数====
 
====テンプレート参照変数====
*#変数名
+
*テンプレート内の特定の要素を参照するための変数
 +
*与えられた変数経由でプロパティなどにアクセス
 +
<pre>
 +
#変数名
 +
</pre>
 
*(change)="0"は、イベントトリガーで値を更新するため必要
 
*(change)="0"は、イベントトリガーで値を更新するため必要
 +
<pre>
 
  &lt;input #txtHoge type="text" (change)="0"/&gt;
 
  &lt;input #txtHoge type="text" (change)="0"/&gt;
 
  &lt;div&gt;{{txtHoge.value}}&lt;/div&gt;
 
  &lt;div&gt;{{txtHoge.value}}&lt;/div&gt;
 +
</pre>
 +
 
===双方向バインディング===
 
===双方向バインディング===
 +
----
 +
*ビューの値とコンポーネントの値を双方向に同期させる
 +
*AngularでForm要素を操作するためには、FormModuleが必要
 
*import と @NgModule の imports に FormModuleを追加
 
*import と @NgModule の imports に FormModuleを追加
 
=====ルートモジュール(app.modules.ts)=====
 
=====ルートモジュール(app.modules.ts)=====
180行目: 250行目:
 
*このためには、name属性の指定が必須
 
*このためには、name属性の指定が必須
 
*ngModelを[(ngModel)]とする
 
*ngModelを[(ngModel)]とする
 +
<pre>
 
  &lt;select name="selAcion" [(ngModel)]="selectedAction"&gt;
 
  &lt;select name="selAcion" [(ngModel)]="selectedAction"&gt;
 
     &lt;option *ngFor="let item of testActions" value="{{item}}" &gt;{{item}}&lt;/option&gt;
 
     &lt;option *ngFor="let item of testActions" value="{{item}}" &gt;{{item}}&lt;/option&gt;
 
  &lt;/select&gt;
 
  &lt;/select&gt;
 +
</pre>
 +
 
=====コンポーネント=====
 
=====コンポーネント=====
 
  export class AccountComponent implements OnInit {
 
  export class AccountComponent implements OnInit {
194行目: 267行目:
 
   [ngModel] = "hogeName"
 
   [ngModel] = "hogeName"
 
   (ngModelChange) = "hogeName=$event.toUpperCase()" /&gt;
 
   (ngModelChange) = "hogeName=$event.toUpperCase()" /&gt;
 +
 +
===イベントバインディング===
 +
----
 +
*ビューからコンポーネントに情報を引き渡す仕組み
 +
*イベントとイベントハンドラーの紐付け
 +
<pre>
 +
<element (event)="exp"></element>
 +
</pre>
 +
*主なevent:click,dblclick,mousedown,mouseup,mouseenter,mousemove,mouseleave,focus,blur,keydown,keypress,keyup,input,select,reset,submit
 +
 +
=====イベント情報を取得する $event=====
 +
*イベントハンドラーを呼び出す際に$eventを明示的に渡す必要がある
 +
<pre>
 +
<input type="button" (click)="show($event)" value="hoge" />
 +
</pre>
 +
*イベント処理後デフォルトの動作をキャンセルするには、event.preventDefault
 +
*イベントバブリングを止めるには、event.stopPropagation
 +
 +
*app.component.ts
 +
import { Component } from '@angular/core';
 +
import { componentFactoryName } from '@angular/compiler';
 +
 +
@Component({
 +
  selector: 'app-root',
 +
  templateUrl: './app.component.html',
 +
  styleUrls: ['./app.component.sass']
 +
})
 +
export class AppComponent {
 +
  title = 'Hot Dog Status';
 +
  status = '';
 +
  save(e: any) {
 +
    this.status = "Save";
 +
  }
 +
  load(e: any) {
 +
    this.status = "Load";
 +
  }
 +
}
 +
.
 +
*appcomponent.html
 +
<pre>
 +
&lt;h1 id="hotDogStatus"&gt;{{title}}:{{status}}&lt;/h1&gt;
 +
&lt;input type="textField" id="latestHotDogStatus" /&gt;
 +
&lt;button (click)="save($event)"&gt;Save&lt;/button&gt;
 +
&lt;button (click)="load($event)"&gt;Load&lt;/button&gt;
 +
 +
&lt;router-outlet&gt;&lt;/router-outlet&gt;
 +
</pre>
 +
[[File:Angular_event.png]]
 +
 +
===属性/クラス/スタイルバインディング===
 +
*コンポーネント -> ビューへのバインディングはプロパティバインディングが基本
 +
*状況によりプロパティバインディングができない場合、またコードが冗長になる場合に備え以下が準備されている
 +
====属性バインディング====
 +
*HTML属性にバインド
 +
 +
<pre>
 +
[attr.name] = "exp"
 +
</pre>
 +
*name:属性名
 +
*exp:任意の式
 +
 +
====クラスバインディング====
 +
*スタイルクラスに特化し、スタイルの着脱をシンプルに表現できる
 +
<pre>
 +
[class.name] = "exp"
 +
</pre>
 +
*name:スタイルクラス名
 +
*exp:任意の式
 +
 +
====スタイルバインディング====
 +
*スタイル属性をバインディングできる
 +
 +
<pre>
 +
[style.name] = "exp"
 +
</pre>
 +
*name:スタイルプロパティ名
 +
*exp:任意の式
 +
 +
*単位付きスタイルプロパティ
 +
<pre>
 +
[style.name.unit] = "exp"
 +
</pre>
 +
*unit:単位
 +
 +
*例
 +
<pre>
 +
[style.font-size.%] = "size"
 +
</pre>
 +
 +
===Form実装例===
 +
====ラジオボタン====
 +
[[File:Angular form radio.png]]
 +
*テンプレート
 +
<pre>
 +
&lt;form #myForm="ngForm" (ngSubmit)="onSubmit(myForm)"&gt;
 +
&lt;ng-container *ngFor="let itm of radioSample; index as i"&gt;
 +
&lt;label&gt;
 +
&lt;input type="radio" name="radioSample"
 +
[(ngModel)]="selected"
 +
[value]="item.value" [checked]="selected == itm.value"
 +
(change)="onRadioChange(i)"&gt;{{ itm.label }}
 +
&lt;/label&gt;
 +
&lt;/ng-container&gt;
 +
&lt;button&gt;submit&lt;/button&gt;
 +
&lt;/form&gt;
 +
</pre>
 +
*コンポーネント
 +
  selected = "3";
 +
  radioSample = [
 +
    {label:'One', value:'1'},
 +
    {label:'Twe', value:'2'},
 +
    {label:'Three', value:'3'},
 +
    {label:'Four', value:'4'},
 +
    {label:'Five', value:'5'}
 +
  ];
 +
  onRadioChange(index:number) {
 +
    console.log(index);
 +
  }
 +
  onSubmit(form: NgForm) {
 +
    console.log(form.value);
 +
  }
 +
 +
==ディレクティブ==
 +
*標準的なHTMLに対して、ngFor、ngStyleなどの独自要素、属性を追加することで機能を付与
 +
*大きく以下の3種類に分類
 +
===分類と主なディレクティブ===
 +
<table  class="wikitable" ><tr><td>種類</td><td>概要</td></tr>
 +
<tr><td>コンポーネント</td><td>テンプレートをともなう</td></tr><tr><td>構造ディレクティブ</td><td>要素を追加削除することで、文書ツリーを操作</td></tr><tr><td>属性ディレクティブ</td><td>属性形式で、要素、コンポーネントの見た目や動作を変更</td></table>
 +
 +
<table  class="wikitable" ><tr><td>分類</td><td>名前</td><td>概要</td></tr>
 +
<tr><td rowspan="5">構造</td><td>ngIf</td><td>真偽により表示切り替え</td></tr>
 +
<tr><td>ngSwitch</td><td>値により表示切り替え</td></tr>
 +
<tr><td>ngFor</td><td>配列のループ処理</td></tr>
 +
<tr><td>ngTemplateOutlet</td><td>用意されたテンプレートの内容をインポート</td></tr>
 +
<tr><td>ngComponentOutlet</td><td>用意されたコンポーネントをインポート</td></tr>
 +
<tr><td rowspan="3">属性</td><td>ngStyle</td><td>要素にスタイルプロパティを付与</td></tr>
 +
<tr><td>ngClass</td><td>要素にスタイルクラスを着脱</td></tr>
 +
<tr><td>ngPlural</td><td>数値に応じて出力切り替え</td>
 +
</table>
 +
 +
===ngIf===
 +
----
 +
*https://angular.jp/api/common/NgIf
 +
*https://qiita.com/KojiTakahara/items/b3fa4e33255e7abc292b
 +
<pre>
 +
<element *ngIf="condition">
 +
contents
 +
</element>
 +
</pre>
 +
*element:任意の要素
 +
*condition:条件式
 +
*contents:条件式が真の時に表示されるコンテンツ
 +
*ngIf=falseは、要素を生成しない(頻繁に表示、非表示を切り替える場合、スタイルバインディングでdisplayを利用する)
 +
 +
*例
 +
*template
 +
<pre>
 +
<mat-toolbar  *ngIf="isToolbar">...</mat-toolbar>
 +
</pre>
 +
*component
 +
<pre>
 +
export class AppComponent {
 +
  isToolbar = true;
 +
    :
 +
</pre>
 +
====テンプレートを切り替える====
 +
*templateはng-templateで宣言できる
 +
<pre>
 +
<div *ngIf="flag; then trueContents; else elseContents>この部分は表示されない</div>
 +
<ng-template #trueContentas>trueの場合表示される</ng-template>
 +
<ng-template #elseContentas>falseの場合表示される</ng-template>
 +
</pre>
 +
===ngSwitch===
 +
----
 +
*指定された式の値に応じて、表示すべきコンテンツを切り替え
 +
*合致がない場合、ngSwitchDefaultを尿字
 +
<pre>
 +
<parent [ngSwitch]="exp">
 +
  <child *ngSwitchCase="value1"> ... </child>
 +
  <child *ngSwitchCase="value2"> ... </child>
 +
                      :
 +
  <child *ngSwitchDefault> ... </child>
 +
</parent>
 +
</pre>
 +
 +
===ngFor===
 +
----
 +
*指定された配列から順に要素を取り出し内容をループする
 +
*指定された要素を繰り返すが、ng-container  というダミーのコンテナに適用すると、含まれる要素一式を繰り返すことができる
 +
*要素の追加、削除をトラッキングするには、トラッキング式(trackBy関数)を利用し、要素を識別させる
 +
<pre>
 +
<element *ngFor="let tmp of list"> ... </element>
 +
</pre>
 +
 +
====ngFor配下で利用できる特殊変数====
 +
*使用する場合、式の中で、index as i のようにローカル変数に代入する必要がある
 +
 +
<table  class="wikitable" ><tr><td>変数</td><td>概要</td></tr><tr><td>index</td><td>ループ回数</td></tr><tr><td>first</td><td>最初の要素か</td></tr><tr><td>last</td><td>最後の要素か</td></tr><tr><td>even</td><td>indexが偶数か</td></tr><tr><td>odd</td><td>indexが奇数か</td></table>
 +
 +
===ngStyle===
 +
----
 +
*複数スタイルをまとめて設定できる
 +
*スタイル指定は、キャメルケースもしくは、ハイフン付き表現を引用符で囲む
 +
<pre>
 +
<element [ngStyle]="objStyle"> ... </element>
 +
</pre>
 +
 +
*例
 +
<pre>
 +
@Compnent({
 +
      :
 +
  template: '<div [ngStyle]="style"></div>'
 +
})
 +
export class AppComponent {
 +
  style = {
 +
    backgroundColor: '#f00',
 +
    'font-weight': 'bold'
 +
  };
 +
}
 +
</pre>
 +
===ngClass===
 +
----
 +
*スタイルクラスを着脱する
 +
<pre>
 +
<element [ngClass]="clazz"> ... </element>
 +
</pre>
 +
 +
*引数(clazz部分)に指定できる形式
 +
<table  class="wikitable" ><tr><td>変数</td><td>概要</td></tr><tr><td>文字列</td><td>スタイルクラス名(空白区切りで複数指定可)</td></tr><tr><td>配列</td><td>スタイルクラス名のリスト</td></tr><tr><td>オブジェクト</td><td>[スタイルクラス名:有効/無効]形式</td></table>
 +
 +
===ngPlural===
 +
----
 +
*式の値がnumに一致する場合、ng-template配下のメッセージを表示する
 +
<pre>
 +
<element [ngPlural]="exp">
 +
  <ng-template ngPluralCase="num">message</ng-template>
 +
      :
 +
</element>
 +
</pre>
 +
*num : =0,=1,other などを指定できる
 +
 +
===ngTemplateOutlet===
 +
----
 +
*予め用意されたテンプレートをコンポーネントの任意の位置に挿入する
 +
<pre>
 +
<ng-container *ngTemplateOutlet="exp; context : ctx"></ng-container>
 +
</pre>
 +
*exp : テンプレート
 +
*ctx : テンプレートに反映させるオブジェクト
 +
 +
=== ngComponentOutlet===
 +
----
 +
*予め用意したコンポーネントを動的にビューにインポートする
 +
<pre>
 +
<ng-container *ngComponentOutlet="exp"></ng-container>
 +
</pre>
 +
 +
==フォーム==
 +
*Angularでは標準のform/input要素が拡張されている
 +
*利用するためには、FormsModuleをインポートする
 +
===ngForm===
 +
----
 +
*Angularでは標準formが拡張されている
 +
*以下のような属性を宣言しておく
 +
<table  class="wikitable" ><tr><td>属性</td><td>概要</td></tr><tr><td>#myForm='"ngForm'"</td><td>ngFormディレクティブを変数にセット</td></tr><tr><td>(ngSubmit)='"show()'"</td><td>サブミット時に呼び出す処理</td></tr><tr><td>novalidate</td><td>HTML5の検証機能を無効化</td></table>
 +
 +
===input===
 +
----
 +
*input/rextareaなどのフォーム要素も拡張されている
 +
<pre>
 +
<input id="main" name="mail" type="email"
 +
  [(ngModel)]="user.mail"
 +
  required email
 +
  #mail="ngModel" />
 +
</pre>
 +
 +
<table  class="wikitable" ><tr><td>属性</td><td>概要</td></tr><tr><td>ngModelディレクティブ</td><td>コンポーネントプロパティとバインド、検証機能を有効化するために必須</td></tr><tr><td>name属性</td><td>Angularが内部的にフォーム要素を識別するためのキー、必ず指定</td></tr><tr><td>#mail='"ngModel'"</td><td>テンプレート参照変数、後からフォーム要素の状態にアクセスできる</td></tr><tr><td>required</td><td>必須</td></tr><tr><td>minlength</td><td>文字列最小値</td></tr><tr><td>maxlenght</td><td>文字列最大値</td></tr><tr><td>email</td><td>メールアドレス形式</td></tr><tr><td>pattern</td><td>正規表現パターンにマッチするか</td></tr><tr><td>min</td><td>数値最小値</td></tr><tr><td>max</td><td>数値最大値</td></table>
 +
 +
====errorsオブジェクト====
 +
*検証の成否を参照
 +
 +
<pre>
 +
入力要素名.errors?.検証型
 +
</pre>
 +
 +
*例
 +
<pre>
 +
mail.errors?.required
 +
</pre>
 +
===状態検知===
 +
<table  class="wikitable" ><tr><td>内容</td><td>書式</td></tr><tr><td>検証項目単位でのエラー有無</td><td>入力要素名.error.検証型</td></tr><tr><td>フォーム/入力項目単位でのエラー有無</td><td>入力要素名.valid、入力要素名.invalid</td></tr><tr><td>フォーム/入力項目項目が変更されていない</td><td>入力要素名.pristine</td></tr><tr><td>フォーム/入力項目項目が変更された</td><td>入力要素名.dirty</td></tr><tr><td>フォーム/入力項目項目にフォーカスが当たった</td><td>入力要素名.touched</td></tr><tr><td>フォーム/入力項目項目にフォーカスが当たっていない</td><td>入力要素名.untouched</td></table>
 +
 +
*Angularでは上記状態に応じて以下のスタイルクラスが設定される
 +
<table  class="wikitable" ><tr><td>スタイルクラス</td><td>概要</td></tr><tr><td>ng-valid</td><td>入力値が妥当</td></tr><tr><td>ng-invalid</td><td>入力値が不正</td></tr><tr><td>ng-pristine</td><td>初期値から変更されていない</td></tr><tr><td>ng-dirty</td><td>初期値から変更された</td></tr><tr><td>ng-touched</td><td>フォーカスが当たったことがある</td></tr><tr><td>ng-untouched</td><td>フォーカスが当たったことがない</td></tr><tr><td>ng-submitted</td><td>サブミットされた</td></table>
 +
 +
*スタイルシートを準備(初期状態でエラーとならないように、ng-dirty & ng-invalid)
 +
<pre>
 +
input.ng-dirty.ng-invalid { background-color: #fee; }
 +
</pre>
 +
 +
*CSSでA,Bどちらも指定されている時に有効:.A.B
 +
*CSSでA,Bどちらかが指定されている時に有効:.A,.B
  
 
==ルーティング==
 
==ルーティング==
261行目: 636行目:
 
  &lt;a routerLink="/account"&gt;Account&lt;/a&gt;
 
  &lt;a routerLink="/account"&gt;Account&lt;/a&gt;
 
  &lt;router-outlet&gt;&lt;/router-outlet&gt;
 
  &lt;router-outlet&gt;&lt;/router-outlet&gt;
 +
===クエリパラメータ===
 +
*http://localhost:4200/?fullscreen=true
 +
<pre>
 +
import { ActivatedRoute } from '@angular/router';
 +
    :
 +
export class AppComponent implements OnInit {
 +
  isToolbar: boolean = true;
 +
 +
  ngOnInit(): void {
 +
    let outer = this;
 +
    this.route.queryParamMap.subscribe(
 +
      paramsMap => {
 +
        const fullscreen = Boolean(paramsMap.get("fullscreen"));
 +
        outer.isToolbar = !fullscreen;
 +
      });
 +
  }
 +
</pre>
 +
 +
==モジュール==
 +
*ある程度の規模の場合、コードをモジュールに分割し、関連クラスをまとめておく
 +
*アプリ構成の見通しがよくなる
 +
*機能の着脱が容易になる
 +
*起動時に呼び出されるモジュールを、ルートモジュール、またはメインモジュールという(QuickStartのapp.module.tsなど)
 +
 +
===定義===
 +
*@NgModuleデコレーターを宣言することで、モジュールと見做される
 +
 +
*デコレーターの主なパラメーター
 +
<table  class="wikitable" ><tr><td>パラメーター名</td><td>概要</td></tr><tr><td>imports</td><td>現在のモジュールで利用する他のモジュール</td></tr><tr><td>exports</td><td>現在のモジュールから外部へ公開するコンポーネントなど</td></tr><tr><td>declarations</td><td>現在のモジュールに属するコンポーネントなど</td></tr><tr><td>bootstrap</td><td>アプリで最初に起動すべき最上位のコンんポーネント</td></tr><tr><td>id</td><td>モジュールのID値</td></table>
 +
 
==コンポーネント==
 
==コンポーネント==
 +
*ページを構成するUI部品
 +
*Angularアプリとは1つ以上のコンポーネントの集合体といえる
 +
*一般的には複数のコンポーネントを組み合わせてページを構成する
 +
 +
===定義===
 +
----
 +
*@Componentデコレーターで宣言する
 +
 +
<pre>
 +
import { Component } from '@angular/core';
 +
 +
@Component({
 +
  selector: 'my-app',
 +
  template: `&lt;h1&gt;Hello {{name}}&kt;/h1&gt;`,
 +
})
 +
export class AppComponent  {
 +
    name = 'Angular';
 +
}
 +
</pre>
 +
*templateに含まれる {{...}} は、Interpolation(補間)と呼ばれ、ビュー変数を埋め込むためのプレースホルダーとして機能する
 +
*Angularでは、コンポーネントの側でデータ(ビュー変数)を用意しておき、テンプレート側でデータを埋め込む場所や表示方法を定義するのが基本となる
 +
*ビュー変数の役割をコンポーネントのプロパティが担う(上記では、nameがプロパティでテンプレートから自由に参照できる変数となる)
 +
 +
 +
{|class="wikitable"
 +
!パラメータ名!!概要
 +
|-
 +
|selector||コンポーネントを適用すべき要素を表す
 +
|-
 +
|template||コンポーネントに適用するビュー
 +
|}
 +
 
===ライフサイクル===
 
===ライフサイクル===
 +
-----
 +
*ライフサイクルメソッドを利用する際は対応するインターフェースを実装する
 +
*インターフェース名は、以下の一覧から ngを除いたもの
 +
 
{|class="wikitable"
 
{|class="wikitable"
 
!ライフサイクル
 
!ライフサイクル
273行目: 714行目:
 
|
 
|
 
|-
 
|-
|ngOnChanges
+
|[https://angular.jp/api/core/OnChanges ngOnChanges]
 
|@Input経由で入力値が設定/再設定された
 
|@Input経由で入力値が設定/再設定された
 
|-
 
|-
 
|ngOnInit
 
|ngOnInit
|入力値(@Inputp)が処理された後、コンポーネントの初期化時(最初のngOnChangesメソッドの後で一度だけ)
+
|ngOnChangesで入力値(@Inputp)が処理された後、コンポーネントの初期化時(最初のngOnChangesメソッドの後で一度だけ)
 +
入力プロパティに基づく処理は、コンストラクタでなく、ここで行う必要がある
 +
一般的にコンポーネントの初期化処理はOnInitに集約する
 
|-
 
|-
 
|ngDoCheck
 
|ngDoCheck
296行目: 739行目:
 
|ngOnDestroyed
 
|ngOnDestroyed
 
|コンポーネントが破棄される時
 
|コンポーネントが破棄される時
 +
タイマーのクリア、Observableの購読解除など
 
|-
 
|-
 
|コンポーネント破棄
 
|コンポーネント破棄
301行目: 745行目:
 
|-
 
|-
 
|}
 
|}
 +
 +
====@ViewChildrenデコレーター :  子コンポーネントを取得====
 +
----
 +
*https://stackoverflow.com/questions/32693061/how-can-i-select-an-element-in-a-component-template
 +
*現在のビューに配置された子コンポーネントを取得する
 +
* ViewChildrenデコレーターを適用したプロパティにはQueryListがセットされます。QueryListはいわゆるコレクションクラスとなっており、検出された複数の要素をより効率的に参照できるようなAPIを提供しています
 +
*[https://angular.io/api/core/QueryList QueryListが提供するAPI]
 +
<pre>
 +
@ViewChildren(child) prop
 +
</pre>
 +
*child: 取得する子コンポーネント、prop:プロパティ名
 +
 +
====@ViewChildデコレーター :  単一の子コンポーネントを取得====
 +
----
 +
*取得すべきコンポーネントが単一であることが分かっている場合は、配列を返す@ViewChildrenではなく、@ViewChildを利用できる
 +
*child: 取得する子コンポーネント、prop:プロパティ名
 +
<pre>
 +
@ViewChild(child) prop
 +
</pre>
 +
 +
===コンポーネントの連携===
 +
----
 +
*複数のコンポーネントが互いに連携して1つの機能を構成
 +
*一般的なアプリケーションでは、複数のコンポーネントが連携して1つの機能(ページ)を構成する
 +
*@Input() と @Output() を使うことで、 親のコンテキストと子のディレクティブやコンポーネントとの間でデータをシェアすることができます。
 +
*@Input() プロパティは書き込み可能である一方、@Output() プロパティは観測可能です。
 +
====@Input デコレーター : コンポーネントを入れ子に配置 ====
 +
----
 +
*https://angular.jp/guide/inputs-outputs
 +
*プロパティ定義に@Inputデコレーターを付与することで、コンポーネントの属性として値を受け取ることができる
 +
 +
*親側から入れ子のコンポーネント属性に値を設定
 +
<pre>
 +
@Component({
 +
  selector: 'parent',
 +
  template: `<div><app-feed-item [feed]="feed"></app-feed-item></div>`
 +
})
 +
export class Parent implements OnInit {
 +
    feed: Feed;
 +
    ngOnInit(): void {
 +
        this.feeds = new Feeds();
 +
    }
 +
}
 +
</pre>
 +
*入れ子になったコンポーネントのプロパティに、@Inputを付与することで、親側でセットした値を受け取ることができる
 +
<pre>
 +
@Component({
 +
  selector: 'app-feed-item',
 +
  template: `<div>{{feed.title}}</div>`
 +
})
 +
export class FeedItemComponent {
 +
  @Input() feed: Feed;
 +
}
 +
</pre>
 +
 +
*セッター、ゲッターに対してデコレーターを付与することもできる
 +
<pre>
 +
export class FeedItemComponent {
 +
  private feed: Feed;
 +
 +
  @Input()
 +
  set Feed(feed:Feed) {
 +
    this.feed = feed;
 +
  }
 +
 
 +
  get Feed() {
 +
    return this.feed;
 +
  }
 +
 +
}
 +
</pre>
 +
 +
====@Outputデコレーター : 子コンポーネントからイベントを受け取る====
 +
----
 +
*https://angular.jp/guide/inputs-outputs
 +
*Outputデコレーターを使用することによって、子コンポーネントのイベントを親コンポーネントへ通知できる
 +
 +
<pre>
 +
@Component({
 +
  selector: 'app-feed-item',
 +
  template: `<div>{{feed.title}}</div>`
 +
})
 +
export class FeedItemComponent {
 +
  @Input() feed: Feed;
 +
  @Output() edited = new EventEmitter<Feed>();
 +
 +
  onSubmit() {
 +
    this.edited.emit(this.feed);
 +
  }
 +
}
 +
</pre>
 +
 +
*親側では、以下で受けられる。
 +
*ここで、$event は、上記Feedが渡される
 +
<pre>
 +
<element (edited)="onEdited($event)"> ... </element>
 +
</pre>
 +
 +
*別名を与えることもできる
 +
<pre>
 +
@Output('changed') edited = new EventEmitter<Feed>();
 +
</pre>
 +
*その場合、受け側は以下
 +
<pre>
 +
<element (changed)="onEdited($event)"> ... </element>
 +
</pre>
 +
===コンポーネント配下のコンテンツをテンプレートに反映させる ng-content===
 +
----
 +
* <ng-content> を使用することで呼び出し元で指定したコンテンツをテンプレートに埋め込むことが可能になる
 +
 +
===コンポーネントのスタイル定義===
 +
----
 +
*@Componentデコレーターの属性に指定する
 +
** styles : スタイルシートの文字列
 +
** styleUrls : cssファイルのパス
 +
*コンポーネントで指定されたコンポーネントスタイルは原則としてコンポーネントの中だけで有効
 +
*コンポーネントスタイルで使用されているセレクター式はコンポーネントローカル
 +
 +
*styles
 +
<pre>
 +
@Component({
 +
  selector: 'app-nav-menu',
 +
  templateUrl: './nav-menu.component.html',
 +
  styles: [`
 +
    a.navbar-brand {
 +
      white-space: normal;
 +
      text-align: center;
 +
      word-break: break-all;
 +
    }
 +
    `,`
 +
    html {
 +
      font-size: 14px;
 +
    }
 +
    `,`
 +
    @media (min-width: 768px) {
 +
      html {
 +
        font-size: 16px;
 +
      }
 +
    }
 +
    `,`
 +
    .box-shadow {
 +
      box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
 +
    }
 +
  `]
 +
})
 +
export class NavMenuComponent {
 +
</pre>
 +
 +
*styleUrls
 +
<pre>
 +
@Component({
 +
  selector: 'app-nav-menu',
 +
  templateUrl: './nav-menu.component.html',
 +
  styleUrls: ['./nav-menu.component.css']
 +
})
 +
export class NavMenuComponent {
 +
</pre>
 +
*その他の定義方法
 +
**テンプレートに style タグを埋め込む
 +
**テンプレートに link タグを埋め込みcssファイルを指定
 +
**スタイルシートから@Importディレクティブによるインポート
 +
 +
====コンポーネントスタイルで利用できる特殊セレクター====
 +
=====コンポーネント本体にスタイルを適用するhost擬似セレクター=====
 +
----
 +
*host擬似セレクターはコンポーネント自体を表す
 +
*テンプレートはコンポーネント配下に展開され、コンポーネントそのものはテンプレートの一部ではない
 +
*コンポーネント要素へはhostセレクター以外ではアクセスできない
 +
<pre>
 +
@Component({
 +
  selector: 'app-fetch-data',
 +
  templateUrl: './fetch-data.component.html',
 +
  styles: [
 +
    `:host {
 +
      display: block;
 +
      border: 3px solid red;
 +
      background-color: Blue;
 +
    }`
 +
  ]
 +
})
 +
export class FetchDataComponent {
 +
    :
 +
</pre>
 +
[[File:angular_host_selector.png | 400px]]
 +
 +
*:host(スタイルクラス名): { ... }  : 特定のスタイルクラス名を持つ場合だけ有効
 +
 +
=====コンポーネントの内部状態に応じてにスタイルを適用するhost-context擬似セレクター=====
 +
----
  
 
==サービス==
 
==サービス==
307行目: 940行目:
 
===登録===
 
===登録===
 
*モジュールにサービスを登録する。
 
*モジュールにサービスを登録する。
*コンポーネントにも登録できる。この場合コンポーネントと子コンポーネントのみで利用できる。
+
*コンポーネントにも登録できる(@Componentデコレーターのprovidersパラメータ)。この場合コンポーネントと子コンポーネントのみで利用できる。
 +
 
 
  import { HogeSer[[vi]]ce } from './hoge.ser[[vi]]ce';
 
  import { HogeSer[[vi]]ce } from './hoge.ser[[vi]]ce';
 
   
 
   
315行目: 949行目:
 
     :
 
     :
 
  })
 
  })
 +
 
===依存性注入===
 
===依存性注入===
 
*方法を宣言するのは、@NgModule/@Component デコレータの pro[[vi]]dersパラメータ
 
*方法を宣言するのは、@NgModule/@Component デコレータの pro[[vi]]dersパラメータ
 
*サービスを提供するためのPro[[vi]]derオブジェクトを登録する
 
*サービスを提供するためのPro[[vi]]derオブジェクトを登録する
 +
 +
====使用する====
 +
*使用するためには、コンストラクタに対応する引数を追加するだけ
 +
*以下の例の場合、this.hogeService.メソッド で利用可能
 +
export class FooComponent {
 +
  constructor(
 +
    private hogeService: HogeService
 +
  ){}
 +
}
 +
 
=====Pro[[vi]]derであることの条件は以下のプロパティを持つこと=====
 
=====Pro[[vi]]derであることの条件は以下のプロパティを持つこと=====
 
{|class="wikitable"
 
{|class="wikitable"
381行目: 1,026行目:
 
     }  
 
     }  
 
  ]
 
  ]
   
+
==パイプ==
 +
*テンプレート上に埋め込まれたデータを加工・整形する
 +
*与えられた式の値を加工/整形する
 +
 
 +
===呼び出し構文===
 +
<pre>
 +
  {{exp | pipe [:param1 [:param2 ..]]}}
 +
</pre>
 +
*式とパイプは、| で区切る
 +
*パイプのパラメーターは、: で列記
 +
*| を重ねることで複数パイプを適用
 +
 
 +
*例
 +
<pre>
 +
{{ price  | currency : '¥' }}
 +
</pre>
 +
 
 +
===標準パイプ===
 +
----
 +
{|class="wikitable"
 +
!パイプ
 +
!内容
 +
|-
 +
|lowercase
 +
|小文字変換
 +
|-
 +
|uppercase
 +
|大文字変換
 +
|-
 +
|titlecase
 +
|単語の先頭を大文字変換
 +
|-
 +
|slice
 +
|文字列から部分文字列を切り出し
 +
|-
 +
|date
 +
|日付・時刻整形
 +
|-
 +
|number
 +
|数値を桁区切り
 +
|-
 +
|percent
 +
|パーセント形式
 +
|-
 +
|currency
 +
|通貨形式
 +
|-
 +
|json
 +
|オブジェクトをJSON変換
 +
|-
 +
|i18nPlural
 +
|数値によって表示文字列をへんか
 +
|-
 +
|i18nSelect
 +
|文字列に応じて出力を切替
 +
|-
 +
|async
 +
|Observable/Promiseによる非同期処理の結果を取得
 +
|}
 +
 
 +
===作成===
 +
----
 +
*小文字に変換してブランクを指定文字に置き換える
 +
<pre>
 +
import { Pipe, PipeTransform } from '@angular/core';
 +
 
 +
@Pipe({
 +
  name: 'globCategory'
 +
})
 +
export class GlobCategoryPipe implements PipeTransform {
 +
 
 +
  transform(value: string, ...args: string[]): string {
 +
    if (value != null) {
 +
      const replacement = ((args != null && args[0] != null)?args[0]:'-');
 +
      value = (value.toLowerCase()).replace(/ /g, replacement);
 +
    }
 +
    return value;
 +
  }
 +
}
 +
</pre>
 +
*使用
 +
<pre>
 +
&lt;a *ngFor="let category of feed.categories" href="https://www.typea.info/blog/index.php/category/{{category | globCategory :'_'}}" target="_blank" class="category-link"&gt;
 +
  {{category}}
 +
&lt;/a&gt;
 +
</pre>
 +
 
 
==Modules==
 
==Modules==
 
===Http===
 
===Http===
 
*https://angular.io/guide/http
 
*https://angular.io/guide/http
==[[R]]xJs==
+
==RxJs==
 +
*RxJsライブラリ
 +
*https://angular.jp/guide/rx-library
 +
 
 +
リアクティブプログラミングは、データストリームと変更の伝播に関する非同期プログラミングのパラダイムです。
 +
RxJS (Reactive Extensions for JavaScript) は、非同期またはコールバックベースのコード (RxJS Docs) の作成を容易にする
 +
observables を使用したリアクティブプログラミング用のライブラリ
 +
 
 +
*非同期処理の既存のコードを observables に変換する
 +
*ストリーム内の値を反復処理する
 +
*異なる型への値のマッピング
 +
*ストリームのフィルタリング
 +
*複数のストリームの作成
 +
 
 +
===Observable 作成関数===
 +
 
 +
ベント、タイマー、promise などから observables を作成するプロセスを簡素化
 +
 
 +
====promise から observable を作成====
 +
import { from } from 'rxjs';
 +
 +
// promise から Observable を作成
 +
const data = from(fetch('/api/endpoint'));
 +
// 非同期の結果の購読を開始
 +
data.subscribe({
 +
  next(response) { console.log(response); },
 +
  error(err) { console.error('Error: ' + err); },
 +
  complete() { console.log('Completed'); }
 +
});
 +
 
 +
====カウンターから observable を作成する====
 +
import { interval } from 'rxjs';
 +
 +
// 一定間隔でで値を発行するObservable を作成
 +
const secondsCounter = interval(1000);
 +
// 発行される値の購読を開始
 +
secondsCounter.subscribe(n =>
 +
  console.log(`It's been ${n} seconds since subscribing!`));
 +
 
 +
====イベントから observable を作成する====
 +
import { fromEvent } from 'rxjs';
 +
 +
const el = document.getElementById('my-element');
 +
 +
// マウスの移動を発行する Observable の作成
 +
const mouseMoves = fromEvent(el, 'mousemove');
 +
 +
// マウス移動イベントを購読開始
 +
const subscription = mouseMoves.subscribe((evt: MouseEvent) => {
 +
  console.log(`Coords: ${evt.clientX} X ${evt.clientY}`);
 +
 +
  // スクリーンの左端をマウスが越えたら購読中止
 +
  if (evt.clientX < 40 && evt.clientY < 40) {
 +
    subscription.unsubscribe();
 +
  }
 +
});
 +
 
 +
====AJAX リクエストから observable を作成====
 +
 
 +
import { ajax } from 'rxjs/ajax';
 +
 +
// AJAXリクエストを生成する Observable の作成
 +
const apiData = ajax('/api/data');
 +
// リクエストの生成を購読
 +
apiData.subscribe(res => console.log(res.status, res.response));
 +
 
 +
===オペレーター===
 +
*オペレーターは、コレクションの高度な操作を可能にするために、observables 基盤上に構築される関数です。たとえば RxJS は map()、filter()、concat()、flatMap() のようなオペレーターを定義
 +
*オペレーターは設定オプションをとり、ソースとなる observable を受け取る関数を返します。この返された関数を実行するとき、オペレーターは observable が出力する値を観測、変換し、変換された値の新しい observable を返します
 +
*[https://christina04.hatenablog.com/entry/2017/02/25/111715 RxのflatMapの使い方]
 +
====Map operator====
 +
import { map } from 'rxjs/operators';
 +
 +
const nums = of(1, 2, 3);
 +
 +
const squareValues = map((val: number) => val * val);
 +
const squaredNums = squareValues(nums);
 +
 +
squaredNums.subscribe(x => console.log(x));
 +
 +
// Logs
 +
// 1
 +
// 4
 +
// 9
 +
 
 +
===パイプ===
 +
 
 +
パイプを使用するとオペレーターをリンクすることができます。パイプを使用すると、複数の機能を1つの機能にまとめることができます。pipe() 関数は、結合する関数を引数としてとり、実行時に順次関数を実行する新しい関数を返します。
 +
 
 +
observable に適用される�オペレーターのセットは、レシピ、つまり関心のある値を生成するための一連の命令です。それだけではレシピは何もしません。レシピを通して結果を出すには subscribe() を呼び出す必要があります。
 +
 
 +
====単独利用====
 +
 
 +
import { filter, map } from 'rxjs/operators';
 +
 +
const nums = of(1, 2, 3, 4, 5);
 +
 +
// Observable を受け入れる関数の生成
 +
const squareOddVals = pipe(
 +
  filter((n: number) => n % 2 !== 0),
 +
  map(n => n * n)
 +
);
 +
 +
// filter と map 関数を実行する Observable を生成
 +
const squareOdd = squareOddVals(nums);
 +
 +
// 結合された関数の実行を購読
 +
squareOdd.subscribe(x => console.log(x));
 +
 
 +
====Observable.pip====
 +
 
 +
pipe() 関数は RxJS Observable のメソッドでもあるため、短く書き換え
 +
 
 +
import { filter, map } from 'rxjs/operators';
 +
 +
const squareOdd = of(1, 2, 3, 4, 5)
 +
  .pipe(
 +
    filter(n => n % 2 !== 0),
 +
    map(n => n * n)
 +
  );
 +
 +
squareOdd.subscribe(x => console.log(x));
 +
 
 
===コンポーネントからサービスのデータを購読する===
 
===コンポーネントからサービスのデータを購読する===
 +
----
 
*https://angular.io/guide/component-interaction#!#bidirectional-ser[[vi]]ce
 
*https://angular.io/guide/component-interaction#!#bidirectional-ser[[vi]]ce
 
=====Ser[[vi]]ce=====
 
=====Ser[[vi]]ce=====
418行目: 1,272行目:
 
   }
 
   }
 
         :
 
         :
 +
 +
==DI==
 +
*https://angular.jp/guide/dependency-injection-in-action
 +
 +
===依存関係を @Optional として @Host で検索を制限===
 +
*コンポーネントが依存関係を要求すると、Angularはそのコンポーネントのインジェクターから開始し、 最初の適切なプロバイダーが見つかるまでインジェクターツリーを検索
 +
*@Optional プロパティデコレーターは、依存関係が見つからない場合にnullを返す
 +
*@Host プロパティデコレーターは、上方向への検索を ホストコンポーネント で停止==
 +
 +
===@Inject を使用してカスタムプロバイダーを設定===
 +
*カスタムプロバイダーを使用すると、組み込みのブラウザAPIなど、暗黙的な依存関係に対する具体的な実装を提供できる
 +
 +
===コンポーネントの DOM 要素を注入===
 +
 +
===プロバイダーを定===
 +
*値プロバイダー: useValue
 +
*クラスプロバイダー: useClass
 +
*エイリアスプロバイダー: useExisting
 +
*ファクトリープロバイダー: useFactory
  
 
==UI==
 
==UI==
424行目: 1,297行目:
 
====レスポンシブレイアウト====
 
====レスポンシブレイアウト====
 
*https://material.angular.io/cdk/layout/over[[vi]]ew
 
*https://material.angular.io/cdk/layout/over[[vi]]ew
 +
 
==テスト==
 
==テスト==
 
===ユニットテスト===
 
===ユニットテスト===
429行目: 1,303行目:
 
*https://karma-runner.github.io/2.0/index.html
 
*https://karma-runner.github.io/2.0/index.html
 
*karma.conf.js
 
*karma.conf.js
 +
 +
==[[Firebase]]==
 +
===Firestoreの利用===
 +
*[https://www.typea.info/blog/index.php/2020/04/04/firebase-hosting-firestore-release/ Firebase Hosting に Angular を統合して Firestore に接続]
 +
 
==[[Tips]]==
 
==[[Tips]]==
 
===機能===
 
===機能===
 +
====router-outlet で @Outputしたい=====
 +
@Outputはできないが、onActivateでコンポーネント参照が取得できる
 +
<pre>
 +
<router-outlet (activate)="onActivate($event)"></router-outlet>
 +
</pre>
 +
*コンポーネント参照のメソッドを呼ぶなりする
 +
<pre>
 +
onActivate(component: any /*Component*/) {
 +
}
 +
</pre>
 +
 +
 +
 +
 
====Chrome拡張機能====
 
====Chrome拡張機能====
 
augury
 
augury
439行目: 1,332行目:
 
====Hello.js 認証をまとめる[[JavaScript]]ライブラリ====
 
====Hello.js 認証をまとめる[[JavaScript]]ライブラリ====
 
*http://adodson.com/hello.js/#scope
 
*http://adodson.com/hello.js/#scope
 +
 
===文法===
 
===文法===
 
====[[Java]]scriptのグローバルオブジェクトを利用====
 
====[[Java]]scriptのグローバルオブジェクトを利用====
454行目: 1,348行目:
 
*https://angular.io/guide/http
 
*https://angular.io/guide/http
 
*[http://typea.info/blg/glob/2018/01/angular-httpclientxsrfmodule-flask-csrf.html Angular + HttpClientXsrfModule + Flask で CSRF]
 
*[http://typea.info/blg/glob/2018/01/angular-httpclientxsrfmodule-flask-csrf.html Angular + HttpClientXsrfModule + Flask で CSRF]
 +
===CORS対応===
 +
*[https://www.typea.info/blog/index.php/2020/11/06/angluar_other_domain_read_xml/ Angular に 別ドメインのブログRSSを読み込み(CORS対応など)]
 
===参照===
 
===参照===
 
*[http://apoc.jp/angular-cli-subdir/ Angular CLIでサブディレクトリにビルドする方法]
 
*[http://apoc.jp/angular-cli-subdir/ Angular CLIでサブディレクトリにビルドする方法]
 +
 +
===Firebase===
 +
*[https://www.typea.info/blog/index.php/2020/05/11/angluarfirebase-auth-google-twitter/ Angluar:Firebase auth でGoogle/Twtterログインを試す]
 +
*[https://www.typea.info/blog/index.php/2020/04/04/firebase-hosting-firestore-release/ Firebase Hosting に Angular を統合して Firestore に接続]

2022年8月22日 (月) 06:31時点における最新版

| AngularJS | TypeScript | Google Cloud Platform | Bootstrap | Flask | ブログカテゴリ(Angular) |

目次

Angular

Angularに関連するブログエントリ=

Angular CLI

準備

Quickスタート

  • ダウンロード
> git clone https://github.com/angular/quickstart.git quickstart
  • パッケージのインストール
> cd quickstart
> npm install
  • 実行
> npm start

Angular quickstart.png

要素

ルートモジュール
要素名 説明
コンポーネント UI部品
サービス ビジネスロジック
パイプ 表示値の加工、演算
ディレクティブ 文書ツリーの操作
  • モジュール
    • Angularにおけるモジュールの実態は、TypeScriptのクラス
    • 定義しただけではモジュールとみなされず、@NgModuleデコレータで宣言が必要

設定ファイル

設定ファイル 概要
package.json 利用するライブラリ情報
tsconfig.json TypeScriptコンパイラーの動作を
systemjs.config.js モジュールローダー(SystemJS)の設定

インストール

>npm install -g @angular/cli

アプリケーションの生成

> ng new myapp

アプリケーションの実行

> ng serve

Angular CLI の主なコマンド


概要 コマンド
appアプリを生成 ng new app
ひな形の生成 ng generate ...
ビルドして起動 ng serve
ビルド ng build
ユニットテスト ng test
E2Eテスト mg e2e
i118nメッセージを抽出 ng xi18n
指定されたキーワードで検索 ng doc keyword
TSLintによるコードチェック ng lint
現在の設定を取得 ng get key
指定されたキー/値を設定 ng set key=value
Angular CLIのバージョン ng version
オブジェクト生成 ng generate サブコマンド

要素 コマンド
モジュール ng g moduole hoge
コンポーネント ng g component hoge
ディレクティブ ng g directive hoge
パイプ ng g pipe hoge
サービス ng g service hoge
ガード ng g guard hoge
クラス ng g class hoge
インターフェース ng g interface hoge
列挙 ng g enum hoge

ng-bootstrap

ngx-bootstrap

Angular-CLIから利用

インストール
npm install ngx-bootstrap bootstrap --save
src/app/app.module.ts の編集
import { AlertModule } from 'ngx-bootstrap';
     :
@NgModule({
     :
   imports: [AlertModule.forRoot(), ... ],
     :
})
.angular-cli.json に以下を追加
"styles": [
  "../node_modules/bootstrap/dist/css/bootstrap.min.css",
  "styles.css"
],
src/app/app.component.html に以下を追加
<alert type="success">hello</alert>

0167 bootstrap alert.jpg

Angular Material

$ ng add @angular/material

カスタムテーマ

非同期通信

app.module.ts
import { HttpModule } from '@angular/http';
    :
@NgModule({
    :
  imports: [
     :
    HttpModule,
  ],
     :
})

データバインディング

構文

データ方向 種類 記法
コンポーネント -> ビュー Interpolation(補間)
{{...}}
コンポーネント -> ビュー プロパティ/属性バインディング
[property]='value'
ビュー -> コンポーネント イベントバインディング
(event)='handler'
コンポーネント <-> ビュー 双方向バインディング
[(target)]='value'
フォーム要素 input/textarea/selectなど,

フォーム要素をバインドするには、ngModel を利用する

<textarea [(ngModel)]="memo"></textarea>


テンプレート参照変数

  • テンプレート内の特定の要素を参照するための変数
  • 与えられた変数経由でプロパティなどにアクセス
#変数名
  • (change)="0"は、イベントトリガーで値を更新するため必要
 <input #txtHoge type="text" (change)="0"/>
 <div>{{txtHoge.value}}</div>

双方向バインディング


  • ビューの値とコンポーネントの値を双方向に同期させる
  • AngularでForm要素を操作するためには、FormModuleが必要
  • import と @NgModule の imports に FormModuleを追加
ルートモジュール(app.modules.ts)
import { FormsModule } from '@angular/forms';
   :
@NgModule({
   :
  imports: [
   BrowserModule,
   FormsModule,
     :
ビュー
  • input/textarea/selectなどフォーム要素をバインドするには、ngModel を利用する
  • このためには、name属性の指定が必須
  • ngModelを[(ngModel)]とする
 <select name="selAcion" [(ngModel)]="selectedAction">
     <option *ngFor="let item of testActions" value="{{item}}" >{{item}}</option>
 </select>
コンポーネント
export class AccountComponent implements OnInit {
  testActions: string[] = [,'login','logout','check'];
  selectedAction: string = ;

入力値の加工

  • 上記は、プロパティバインディング、イベントバインディングを組み合わせて、双方向を実現している。
  • データバインディング時に値を加工する場合、[(ngModel)] を [ngModel]と(ngModelChagen) に分解
  • $eventは入力値そのものを表す
<input name="hoge" type="text"
  [ngModel] = "hogeName"
  (ngModelChange) = "hogeName=$event.toUpperCase()" />

イベントバインディング


  • ビューからコンポーネントに情報を引き渡す仕組み
  • イベントとイベントハンドラーの紐付け
<element (event)="exp"></element>
  • 主なevent:click,dblclick,mousedown,mouseup,mouseenter,mousemove,mouseleave,focus,blur,keydown,keypress,keyup,input,select,reset,submit
イベント情報を取得する $event
  • イベントハンドラーを呼び出す際に$eventを明示的に渡す必要がある
<input type="button" (click)="show($event)" value="hoge" />
  • イベント処理後デフォルトの動作をキャンセルするには、event.preventDefault
  • イベントバブリングを止めるには、event.stopPropagation
  • app.component.ts
import { Component } from '@angular/core';
import { componentFactoryName } from '@angular/compiler';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.sass']
})
export class AppComponent {
  title = 'Hot Dog Status';
  status = ;
  save(e: any) {
    this.status = "Save";
  }
  load(e: any) {
    this.status = "Load";
  }
}

.

  • appcomponent.html
 <h1 id="hotDogStatus">{{title}}:{{status}}</h1>
 <input type="textField" id="latestHotDogStatus" />
 <button (click)="save($event)">Save</button>
 <button (click)="load($event)">Load</button>
 
 <router-outlet></router-outlet>

Angular event.png

属性/クラス/スタイルバインディング

  • コンポーネント -> ビューへのバインディングはプロパティバインディングが基本
  • 状況によりプロパティバインディングができない場合、またコードが冗長になる場合に備え以下が準備されている

属性バインディング

  • HTML属性にバインド
[attr.name] = "exp"
  • name:属性名
  • exp:任意の式

クラスバインディング

  • スタイルクラスに特化し、スタイルの着脱をシンプルに表現できる
[class.name] = "exp"
  • name:スタイルクラス名
  • exp:任意の式

スタイルバインディング

  • スタイル属性をバインディングできる
[style.name] = "exp"
  • name:スタイルプロパティ名
  • exp:任意の式
  • 単位付きスタイルプロパティ
[style.name.unit] = "exp"
  • unit:単位
[style.font-size.%] = "size"

Form実装例

ラジオボタン

Angular form radio.png

  • テンプレート
 <form #myForm="ngForm" (ngSubmit)="onSubmit(myForm)">
 	<ng-container *ngFor="let itm of radioSample; index as i">
 		<label>
 			<input type="radio" name="radioSample"
 				[(ngModel)]="selected"
 				[value]="item.value" [checked]="selected == itm.value"
 				(change)="onRadioChange(i)">{{ itm.label }}
 		</label>
 	</ng-container>
 	<button>submit</button>
 </form>
  • コンポーネント
 selected = "3";
 radioSample = [
   {label:'One', value:'1'},
   {label:'Twe', value:'2'},
   {label:'Three', value:'3'},
   {label:'Four', value:'4'},
   {label:'Five', value:'5'}
 ];
 onRadioChange(index:number) {
   console.log(index);
 }
 onSubmit(form: NgForm) {
   console.log(form.value);
 }

ディレクティブ

  • 標準的なHTMLに対して、ngFor、ngStyleなどの独自要素、属性を追加することで機能を付与
  • 大きく以下の3種類に分類

分類と主なディレクティブ

種類概要
コンポーネントテンプレートをともなう
構造ディレクティブ要素を追加削除することで、文書ツリーを操作
属性ディレクティブ属性形式で、要素、コンポーネントの見た目や動作を変更
分類名前概要
構造ngIf真偽により表示切り替え
ngSwitch値により表示切り替え
ngFor配列のループ処理
ngTemplateOutlet用意されたテンプレートの内容をインポート
ngComponentOutlet用意されたコンポーネントをインポート
属性ngStyle要素にスタイルプロパティを付与
ngClass要素にスタイルクラスを着脱
ngPlural数値に応じて出力切り替え

ngIf


<element *ngIf="condition">
contents
</element>
  • element:任意の要素
  • condition:条件式
  • contents:条件式が真の時に表示されるコンテンツ
  • ngIf=falseは、要素を生成しない(頻繁に表示、非表示を切り替える場合、スタイルバインディングでdisplayを利用する)
  • template
<mat-toolbar  *ngIf="isToolbar">...</mat-toolbar>
  • component
export class AppComponent {
  isToolbar = true;
    :

テンプレートを切り替える

  • templateはng-templateで宣言できる
<div *ngIf="flag; then trueContents; else elseContents>この部分は表示されない</div>
<ng-template #trueContentas>trueの場合表示される</ng-template>
<ng-template #elseContentas>falseの場合表示される</ng-template>

ngSwitch


  • 指定された式の値に応じて、表示すべきコンテンツを切り替え
  • 合致がない場合、ngSwitchDefaultを尿字
<parent [ngSwitch]="exp">
  <child *ngSwitchCase="value1"> ... </child>
  <child *ngSwitchCase="value2"> ... </child>
                      :
  <child *ngSwitchDefault> ... </child>
</parent>

ngFor


  • 指定された配列から順に要素を取り出し内容をループする
  • 指定された要素を繰り返すが、ng-container というダミーのコンテナに適用すると、含まれる要素一式を繰り返すことができる
  • 要素の追加、削除をトラッキングするには、トラッキング式(trackBy関数)を利用し、要素を識別させる
<element *ngFor="let tmp of list"> ... </element>

ngFor配下で利用できる特殊変数

  • 使用する場合、式の中で、index as i のようにローカル変数に代入する必要がある
変数概要
indexループ回数
first最初の要素か
last最後の要素か
evenindexが偶数か
oddindexが奇数か

ngStyle


  • 複数スタイルをまとめて設定できる
  • スタイル指定は、キャメルケースもしくは、ハイフン付き表現を引用符で囲む
<element [ngStyle]="objStyle"> ... </element>
@Compnent({
      :
   template: '<div [ngStyle]="style"></div>'
})
export class AppComponent {
  style = {
     backgroundColor: '#f00',
     'font-weight': 'bold' 
  };
}

ngClass


  • スタイルクラスを着脱する
<element [ngClass]="clazz"> ... </element>
  • 引数(clazz部分)に指定できる形式
変数概要
文字列スタイルクラス名(空白区切りで複数指定可)
配列スタイルクラス名のリスト
オブジェクト[スタイルクラス名:有効/無効]形式

ngPlural


  • 式の値がnumに一致する場合、ng-template配下のメッセージを表示する
<element [ngPlural]="exp">
  <ng-template ngPluralCase="num">message</ng-template>
      :
</element>
  • num : =0,=1,other などを指定できる

ngTemplateOutlet


  • 予め用意されたテンプレートをコンポーネントの任意の位置に挿入する
<ng-container *ngTemplateOutlet="exp; context : ctx"></ng-container>
  • exp : テンプレート
  • ctx : テンプレートに反映させるオブジェクト

ngComponentOutlet


  • 予め用意したコンポーネントを動的にビューにインポートする
<ng-container *ngComponentOutlet="exp"></ng-container>

フォーム

  • Angularでは標準のform/input要素が拡張されている
  • 利用するためには、FormsModuleをインポートする

ngForm


  • Angularでは標準formが拡張されている
  • 以下のような属性を宣言しておく
属性概要
#myForm='"ngForm'"ngFormディレクティブを変数にセット
(ngSubmit)='"show()'"サブミット時に呼び出す処理
novalidateHTML5の検証機能を無効化

input


  • input/rextareaなどのフォーム要素も拡張されている
<input id="main" name="mail" type="email"
   [(ngModel)]="user.mail"
   required email
   #mail="ngModel" />
属性概要
ngModelディレクティブコンポーネントプロパティとバインド、検証機能を有効化するために必須
name属性Angularが内部的にフォーム要素を識別するためのキー、必ず指定
#mail='"ngModel'"テンプレート参照変数、後からフォーム要素の状態にアクセスできる
required必須
minlength文字列最小値
maxlenght文字列最大値
emailメールアドレス形式
pattern正規表現パターンにマッチするか
min数値最小値
max数値最大値

errorsオブジェクト

  • 検証の成否を参照
入力要素名.errors?.検証型
mail.errors?.required

状態検知

内容書式
検証項目単位でのエラー有無入力要素名.error.検証型
フォーム/入力項目単位でのエラー有無入力要素名.valid、入力要素名.invalid
フォーム/入力項目項目が変更されていない入力要素名.pristine
フォーム/入力項目項目が変更された入力要素名.dirty
フォーム/入力項目項目にフォーカスが当たった入力要素名.touched
フォーム/入力項目項目にフォーカスが当たっていない入力要素名.untouched
  • Angularでは上記状態に応じて以下のスタイルクラスが設定される
スタイルクラス概要
ng-valid入力値が妥当
ng-invalid入力値が不正
ng-pristine初期値から変更されていない
ng-dirty初期値から変更された
ng-touchedフォーカスが当たったことがある
ng-untouchedフォーカスが当たったことがない
ng-submittedサブミットされた
  • スタイルシートを準備(初期状態でエラーとならないように、ng-dirty & ng-invalid)
input.ng-dirty.ng-invalid { background-color: #fee; }
  • CSSでA,Bどちらも指定されている時に有効:.A.B
  • CSSでA,Bどちらかが指定されている時に有効:.A,.B

ルーティング

適用

プロジェクト作成

> ng new app --routing
--routing オプションを付与せずにプロジェクトを作成した場合
  • /src/app/app-routing.module.ts を追加
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [];

@NgModule({
  imports: [
    RouterModule.forRoot(routes),
  ],
  exports: [RouterModule]
})
export class AppRoutingModule { }
  • /src/app/app.module.ts
import { AppRoutingModule } from './app-routing.module';
   :
@NgModule({
  imports: [
    BrowserModule,
    AppRoutingModule, 
  ],
  providers: [],
  • /src/app/app.component.spec.ts
import { RouterTestingModule } from '@angular/router/testing';
   :
describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule
      ],
      declarations: [
        AppComponent
      ],
    }).compileComponents();
  }));

コンポーネント作成

> ng g component account --routing

ルーティング定義

  • app-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AccountComponent } from './account/account.component'

const routes: Routes = [
  {
    path: 'account', component: AccountComponent
  }
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes),
  ],
  exports: [RouterModule]
})
export class AppRoutingModule { }

利用

<a routerLink="/account">Account</a>
<router-outlet></router-outlet>

クエリパラメータ

import { ActivatedRoute } from '@angular/router';
     :
export class AppComponent implements OnInit {
  isToolbar: boolean = true;

  ngOnInit(): void {
    let outer = this;
    this.route.queryParamMap.subscribe(
      paramsMap => {
        const fullscreen = Boolean(paramsMap.get("fullscreen"));
        outer.isToolbar = !fullscreen;
      });
  }

モジュール

  • ある程度の規模の場合、コードをモジュールに分割し、関連クラスをまとめておく
  • アプリ構成の見通しがよくなる
  • 機能の着脱が容易になる
  • 起動時に呼び出されるモジュールを、ルートモジュール、またはメインモジュールという(QuickStartのapp.module.tsなど)

定義

  • @NgModuleデコレーターを宣言することで、モジュールと見做される
  • デコレーターの主なパラメーター
パラメーター名概要
imports現在のモジュールで利用する他のモジュール
exports現在のモジュールから外部へ公開するコンポーネントなど
declarations現在のモジュールに属するコンポーネントなど
bootstrapアプリで最初に起動すべき最上位のコンんポーネント
idモジュールのID値

コンポーネント

  • ページを構成するUI部品
  • Angularアプリとは1つ以上のコンポーネントの集合体といえる
  • 一般的には複数のコンポーネントを組み合わせてページを構成する

定義


  • @Componentデコレーターで宣言する
 import { Component } from '@angular/core';
 
 @Component({
   selector: 'my-app',
   template: `<h1>Hello {{name}}&kt;/h1>`,
 })
 export class AppComponent  { 
     name = 'Angular'; 
 }
  • templateに含まれる テンプレート:... は、Interpolation(補間)と呼ばれ、ビュー変数を埋め込むためのプレースホルダーとして機能する
  • Angularでは、コンポーネントの側でデータ(ビュー変数)を用意しておき、テンプレート側でデータを埋め込む場所や表示方法を定義するのが基本となる
  • ビュー変数の役割をコンポーネントのプロパティが担う(上記では、nameがプロパティでテンプレートから自由に参照できる変数となる)


パラメータ名 概要
selector コンポーネントを適用すべき要素を表す
template コンポーネントに適用するビュー

ライフサイクル


  • ライフサイクルメソッドを利用する際は対応するインターフェースを実装する
  • インターフェース名は、以下の一覧から ngを除いたもの
ライフサイクル 内容
コンポーネント生成
コンストラクター
ngOnChanges @Input経由で入力値が設定/再設定された
ngOnInit ngOnChangesで入力値(@Inputp)が処理された後、コンポーネントの初期化時(最初のngOnChangesメソッドの後で一度だけ)

入力プロパティに基づく処理は、コンストラクタでなく、ここで行う必要がある 一般的にコンポーネントの初期化処理はOnInitに集約する

ngDoCheck 状態の変更を検出したとき
ngAfterContentInit 外部コンテンツを初期化した時(最初のngDoCheckメソッドの後で一度だけ)
ngAfterContentChecked 外部コンテンツの変更をチェックした時
ngAfterViewInit 現在のコンポーネントと子コンポーネントのビューを生成した時(最初のngAfterContentCheckedメソッドの後で一度だけ)
ngAfterViewChecked 現在のコンポーネントと子コンポーネントのビューが変更された時
ngOnDestroyed コンポーネントが破棄される時

タイマーのクリア、Observableの購読解除など

コンポーネント破棄

@ViewChildrenデコレーター : 子コンポーネントを取得


@ViewChildren(child) prop
  • child: 取得する子コンポーネント、prop:プロパティ名

@ViewChildデコレーター : 単一の子コンポーネントを取得


  • 取得すべきコンポーネントが単一であることが分かっている場合は、配列を返す@ViewChildrenではなく、@ViewChildを利用できる
  • child: 取得する子コンポーネント、prop:プロパティ名
@ViewChild(child) prop

コンポーネントの連携


  • 複数のコンポーネントが互いに連携して1つの機能を構成
  • 一般的なアプリケーションでは、複数のコンポーネントが連携して1つの機能(ページ)を構成する
  • @Input() と @Output() を使うことで、 親のコンテキストと子のディレクティブやコンポーネントとの間でデータをシェアすることができます。
  • @Input() プロパティは書き込み可能である一方、@Output() プロパティは観測可能です。

@Input デコレーター : コンポーネントを入れ子に配置


  • 親側から入れ子のコンポーネント属性に値を設定
@Component({
  selector: 'parent',
  template: `<div><app-feed-item [feed]="feed"></app-feed-item></div>`
})
export class Parent implements OnInit {
    feed: Feed;
    ngOnInit(): void {
        this.feeds = new Feeds();
    }
}
  • 入れ子になったコンポーネントのプロパティに、@Inputを付与することで、親側でセットした値を受け取ることができる
@Component({
  selector: 'app-feed-item',
  template: `<div>{{feed.title}}</div>`
})
export class FeedItemComponent {
  @Input() feed: Feed;
}
  • セッター、ゲッターに対してデコレーターを付与することもできる
export class FeedItemComponent {
  private feed: Feed;

  @Input()
  set Feed(feed:Feed) {
     this.feed = feed;
  }
   
  get Feed() {
    return this.feed;
  }

}

@Outputデコレーター : 子コンポーネントからイベントを受け取る


@Component({
  selector: 'app-feed-item',
  template: `<div>{{feed.title}}</div>`
})
export class FeedItemComponent {
  @Input() feed: Feed;
  @Output() edited = new EventEmitter<Feed>();

  onSubmit() {
    this.edited.emit(this.feed);
  }
}
  • 親側では、以下で受けられる。
  • ここで、$event は、上記Feedが渡される
<element (edited)="onEdited($event)"> ... </element>
  • 別名を与えることもできる
@Output('changed') edited = new EventEmitter<Feed>();
  • その場合、受け側は以下
<element (changed)="onEdited($event)"> ... </element>

コンポーネント配下のコンテンツをテンプレートに反映させる ng-content


  • <ng-content> を使用することで呼び出し元で指定したコンテンツをテンプレートに埋め込むことが可能になる

コンポーネントのスタイル定義


  • @Componentデコレーターの属性に指定する
    • styles : スタイルシートの文字列
    • styleUrls : cssファイルのパス
  • コンポーネントで指定されたコンポーネントスタイルは原則としてコンポーネントの中だけで有効
  • コンポーネントスタイルで使用されているセレクター式はコンポーネントローカル
  • styles
@Component({
  selector: 'app-nav-menu',
  templateUrl: './nav-menu.component.html',
  styles: [`
    a.navbar-brand {
      white-space: normal;
      text-align: center;
      word-break: break-all;
    }
    `,`
    html {
      font-size: 14px;
    }
    `,`
    @media (min-width: 768px) {
      html {
        font-size: 16px;
      }
    }
    `,`
    .box-shadow {
      box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
    }
  `]
})
export class NavMenuComponent {
  • styleUrls
@Component({
  selector: 'app-nav-menu',
  templateUrl: './nav-menu.component.html',
  styleUrls: ['./nav-menu.component.css']
})
export class NavMenuComponent {
  • その他の定義方法
    • テンプレートに style タグを埋め込む
    • テンプレートに link タグを埋め込みcssファイルを指定
    • スタイルシートから@Importディレクティブによるインポート

コンポーネントスタイルで利用できる特殊セレクター

コンポーネント本体にスタイルを適用するhost擬似セレクター

  • host擬似セレクターはコンポーネント自体を表す
  • テンプレートはコンポーネント配下に展開され、コンポーネントそのものはテンプレートの一部ではない
  • コンポーネント要素へはhostセレクター以外ではアクセスできない
@Component({
  selector: 'app-fetch-data',
  templateUrl: './fetch-data.component.html',
  styles: [
    `:host {
      display: block;
      border: 3px solid red;
      background-color: Blue;
    }`
  ]
})
export class FetchDataComponent {
    :

Angular host selector.png

  • host(スタイルクラス名): { ... } : 特定のスタイルクラス名を持つ場合だけ有効
コンポーネントの内部状態に応じてにスタイルを適用するhost-context擬似セレクター

サービス

  • サービスクラスであることの条件は、@Injectable デコレータを付与することのみ。
  • @Injectable デコレータは、コンポーネントに対してサービスを引き渡せることを意味する。

登録

  • モジュールにサービスを登録する。
  • コンポーネントにも登録できる(@Componentデコレーターのprovidersパラメータ)。この場合コンポーネントと子コンポーネントのみで利用できる。
import { HogeService } from './hoge.service';

@NgModule({
   :
  providers: [HogeService],
   :
})

依存性注入

  • 方法を宣言するのは、@NgModule/@Component デコレータの providersパラメータ
  • サービスを提供するためのProviderオブジェクトを登録する

使用する

  • 使用するためには、コンストラクタに対応する引数を追加するだけ
  • 以下の例の場合、this.hogeService.メソッド で利用可能
export class FooComponent {
  constructor(
    private hogeService: HogeService
  ){}
}
Providerであることの条件は以下のプロパティを持つこと
プロパティ 内容
provide サービスを注入する際に利用するDIトークン
useXxxxx サービスの生成方法 例 userClass: XXXX と指定するとクラス XXXX を常にnew でインスタンス化する
multi 同一のDIトークンに対して複数のProviderを追加するか

useXxxx

プロパティ 内容
useClass 指定されたクラスを注入のたびにインスタンス化
useValue 指定されたオブジェクトを常に引き渡す(同じ値になる)
useExisting 指定されたトークンのエイリアスを生成
useFactory 指定されたファクトリー関数で注入の際にオブジェクトを生成
useClass
  • 常に新たなインスタンスを生成する
providers: [
  { provide: HogeService, useClass: HogeService } 
]
useValue
  • 常に同じオブジェクトを注入する
  • クラスのインスタンスを渡す
providers: [
  { provide: HogeService, useVlaue: new HogeService() } 
]
useExisting
  • トークンのエイリアスを生成
providers: [
  { provide: HogeService, useClass: HogeService }、
  { provide: HogeAliasService, useExisting: HogeService }、
]

<blockquote>互換性維持など、別のトークンから同一インスタンスを取得したい場合などに利用</blockquote>

useFactory
  • ファクトリー関数経由でインスタンスを生成
providers: [
  { provide: HogeService, useFactory: () => {
      let service = new HogeService();
      service.foo = "bar";
      return service;
    } 
   } 
]

パイプ

  • テンプレート上に埋め込まれたデータを加工・整形する
  • 与えられた式の値を加工/整形する

呼び出し構文

 {{exp | pipe [:param1 [:param2 ..]]}}
  • 式とパイプは、| で区切る
  • パイプのパラメーターは、: で列記
  • | を重ねることで複数パイプを適用
{{ price  | currency : '¥' }}

標準パイプ


パイプ 内容
lowercase 小文字変換
uppercase 大文字変換
titlecase 単語の先頭を大文字変換
slice 文字列から部分文字列を切り出し
date 日付・時刻整形
number 数値を桁区切り
percent パーセント形式
currency 通貨形式
json オブジェクトをJSON変換
i18nPlural 数値によって表示文字列をへんか
i18nSelect 文字列に応じて出力を切替
async Observable/Promiseによる非同期処理の結果を取得

作成


  • 小文字に変換してブランクを指定文字に置き換える
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'globCategory'
})
export class GlobCategoryPipe implements PipeTransform {

  transform(value: string, ...args: string[]): string {
    if (value != null) {
      const replacement = ((args != null && args[0] != null)?args[0]:'-');
      value = (value.toLowerCase()).replace(/ /g, replacement);
    }
    return value;
  }
}
  • 使用
<a *ngFor="let category of feed.categories" href="https://www.typea.info/blog/index.php/category/{{category | globCategory :'_'}}" target="_blank" class="category-link">
  {{category}}
</a>

Modules

Http

RxJs

リアクティブプログラミングは、データストリームと変更の伝播に関する非同期プログラミングのパラダイムです。 RxJS (Reactive Extensions for JavaScript) は、非同期またはコールバックベースのコード (RxJS Docs) の作成を容易にする observables を使用したリアクティブプログラミング用のライブラリ

  • 非同期処理の既存のコードを observables に変換する
  • ストリーム内の値を反復処理する
  • 異なる型への値のマッピング
  • ストリームのフィルタリング
  • 複数のストリームの作成

Observable 作成関数

ベント、タイマー、promise などから observables を作成するプロセスを簡素化

promise から observable を作成

import { from } from 'rxjs';

// promise から Observable を作成
const data = from(fetch('/api/endpoint'));
// 非同期の結果の購読を開始
data.subscribe({
  next(response) { console.log(response); },
  error(err) { console.error('Error: ' + err); },
  complete() { console.log('Completed'); }
});

カウンターから observable を作成する

import { interval } from 'rxjs';

// 一定間隔でで値を発行するObservable を作成
const secondsCounter = interval(1000);
// 発行される値の購読を開始
secondsCounter.subscribe(n =>
  console.log(`It's been ${n} seconds since subscribing!`));

イベントから observable を作成する

import { fromEvent } from 'rxjs';

const el = document.getElementById('my-element');

// マウスの移動を発行する Observable の作成
const mouseMoves = fromEvent(el, 'mousemove');

// マウス移動イベントを購読開始 
const subscription = mouseMoves.subscribe((evt: MouseEvent) => {
  console.log(`Coords: ${evt.clientX} X ${evt.clientY}`);

  // スクリーンの左端をマウスが越えたら購読中止
  if (evt.clientX < 40 && evt.clientY < 40) {
    subscription.unsubscribe();
  }
});

AJAX リクエストから observable を作成

import { ajax } from 'rxjs/ajax';

// AJAXリクエストを生成する Observable の作成
const apiData = ajax('/api/data');
// リクエストの生成を購読
apiData.subscribe(res => console.log(res.status, res.response));

オペレーター

  • オペレーターは、コレクションの高度な操作を可能にするために、observables 基盤上に構築される関数です。たとえば RxJS は map()、filter()、concat()、flatMap() のようなオペレーターを定義
  • オペレーターは設定オプションをとり、ソースとなる observable を受け取る関数を返します。この返された関数を実行するとき、オペレーターは observable が出力する値を観測、変換し、変換された値の新しい observable を返します
  • RxのflatMapの使い方

Map operator

import { map } from 'rxjs/operators';

const nums = of(1, 2, 3);

const squareValues = map((val: number) => val * val);
const squaredNums = squareValues(nums);

squaredNums.subscribe(x => console.log(x));

// Logs
// 1
// 4
// 9

パイプ

パイプを使用するとオペレーターをリンクすることができます。パイプを使用すると、複数の機能を1つの機能にまとめることができます。pipe() 関数は、結合する関数を引数としてとり、実行時に順次関数を実行する新しい関数を返します。

observable に適用される�オペレーターのセットは、レシピ、つまり関心のある値を生成するための一連の命令です。それだけではレシピは何もしません。レシピを通して結果を出すには subscribe() を呼び出す必要があります。

単独利用

import { filter, map } from 'rxjs/operators';

const nums = of(1, 2, 3, 4, 5);

// Observable を受け入れる関数の生成
const squareOddVals = pipe(
  filter((n: number) => n % 2 !== 0),
  map(n => n * n)
);

// filter と map 関数を実行する Observable を生成
const squareOdd = squareOddVals(nums);

// 結合された関数の実行を購読
squareOdd.subscribe(x => console.log(x));

Observable.pip

pipe() 関数は RxJS Observable のメソッドでもあるため、短く書き換え

import { filter, map } from 'rxjs/operators';

const squareOdd = of(1, 2, 3, 4, 5)
  .pipe(
    filter(n => n % 2 !== 0),
    map(n => n * n)
  );

squareOdd.subscribe(x => console.log(x));

コンポーネントからサービスのデータを購読する


Service
import { Subject } from 'rxjs/subject';
import { User } from './user';
 
@Injectable()
export class AccountService {
  private userChangeAnnouncedSource = new Subject<User>();
  userChangeAnnounced$ = this.userChangeAnnouncedSource.asObservable();
    :
  announceUserChange(user: User) {
    this.userChangeAnnouncedSource.next(user);
  }
    :
Component
import { Subscription } from 'rxjs/Subscription';
  
export class AccountComponent implements OnInit, OnDestroy {
    user: User;
    subscription: Subscription

  ngOnInit() {
    this.subscription = this.accountService.userChangeAnnounced$.subscribe((user:User) => {
      this.user = user;
    });  
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
       :

DI

依存関係を @Optional として @Host で検索を制限

  • コンポーネントが依存関係を要求すると、Angularはそのコンポーネントのインジェクターから開始し、 最初の適切なプロバイダーが見つかるまでインジェクターツリーを検索
  • @Optional プロパティデコレーターは、依存関係が見つからない場合にnullを返す
  • @Host プロパティデコレーターは、上方向への検索を ホストコンポーネント で停止==

@Inject を使用してカスタムプロバイダーを設定

  • カスタムプロバイダーを使用すると、組み込みのブラウザAPIなど、暗黙的な依存関係に対する具体的な実装を提供できる

コンポーネントの DOM 要素を注入

プロバイダーを定

  • 値プロバイダー: useValue
  • クラスプロバイダー: useClass
  • エイリアスプロバイダー: useExisting
  • ファクトリープロバイダー: useFactory

UI

Angular Material

レスポンシブレイアウト

テスト

ユニットテスト

Karma

Firebase

Firestoreの利用

Tips

機能

router-outlet で @Outputしたい=

@Outputはできないが、onActivateでコンポーネント参照が取得できる

<router-outlet (activate)="onActivate($event)"></router-outlet>
  • コンポーネント参照のメソッドを呼ぶなりする
onActivate(component: any /*Component*/) {
}



Chrome拡張機能

augury 0168 chrome extension.jpg

Facebook SDK

Hello.js 認証をまとめるJavaScriptライブラリ

文法

Javascriptのグローバルオブジェクトを利用

declare const hoge;

コード

コードからルーティング

import { Router } from '@angular/router';
   :
constructor(private router: Router) {}
   :
this.router.navigate(['/hoge']) 

CSRF

CORS対応

参照

Firebase