Angular
| AngularJS | TypeScript | Google Cloud Platform | Bootstrap | Flask | ブログカテゴリ(Angular) |
目次
Angular
Angular CLI
準備
- Node.jsのインストール
Quickスタート
- ダウンロード
> git clone https://github.com/angular/quickstart.git quickstart
- パッケージのインストール
> cd quickstart > npm install
- 実行
> npm start
要素
要素名 | 説明 |
---|---|
コンポーネント | 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 |
ngx-bootstrap
- https://valor-software.com/ngx-bootstrap
- BootstrapをAgularアプリケーションから利用
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>
Angular Material
$ ng add @angular/material
カスタムテーマ
- https://www.creative-tim.com/product/material-dashboard-angular2
- Angular Material でカスタムカラーを使う
- カラーパレット生成
- カラーシステム
- Angular Materialのカスタムテーマを設定
- Angular Material _palette.scss
非同期通信
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>
属性/クラス/スタイルバインディング
- コンポーネント -> ビューへのバインディングはプロパティバインディングが基本
- 状況によりプロパティバインディングができない場合、またコードが冗長になる場合に備え以下が準備されている
属性バインディング
- 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実装例
ラジオボタン
- テンプレート
<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); }
ディレクティブ
NgIf
表示非表示切り替え
- https://qiita.com/KojiTakahara/items/b3fa4e33255e7abc292b
- ngIf=falseは、要素を生成しない
- template
<mat-toolbar *ngIf="isToolbar">...</mat-toolbar>
- component
export class AppComponent { isToolbar = true; :
ルーティング
適用
プロジェクト作成
> 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 | コンポーネントに適用するビュー |
ライフサイクル
ライフサイクル | 内容 |
---|---|
コンポーネント生成 | |
コンストラクター | |
ngOnChanges | @Input経由で入力値が設定/再設定された |
ngOnInit | 入力値(@Inputp)が処理された後、コンポーネントの初期化時(最初のngOnChangesメソッドの後で一度だけ) |
ngDoCheck | 状態の変更を検出したとき |
ngAfterContentInit | 外部コンテンツを初期化した時(最初のngDoCheckメソッドの後で一度だけ) |
ngAfterContentChecked | 外部コンテンツの変更をチェックした時 |
ngAfterViewInit | 現在のコンポーネントと子コンポーネントのビューを生成した時(最初のngAfterContentCheckedメソッドの後で一度だけ) |
ngAfterViewChecked | 現在のコンポーネントと子コンポーネントのビューが変更された時 |
ngOnDestroyed | コンポーネントが破棄される時 |
コンポーネント破棄 |
@ViewChildrenデコレーター : 子コンポーネントを取得
- https://stackoverflow.com/questions/32693061/how-can-i-select-an-element-in-a-component-template
- 現在のビューに配置された子コンポーネントを取得する
- child: 取得する子コンポーネント、prop:プロパティ名
@ViewChildren(child) prop
@ViewChildデコレーター : 単一の子コンポーネントを取得
- 取得すべきコンポーネントが単一であることが分かっている場合は、配列を返す@ViewChildrenではなく、@ViewChildを利用できる
- child: 取得する子コンポーネント、prop:プロパティ名
@ViewChild(child) prop
コンポーネントの連携
- 複数のコンポーネントが互いに連携して1つの機能を構成
- @Input() と @Output() を使うことで、 親のコンテキストと子のディレクティブやコンポーネントとの間でデータをシェアすることができます。
- @Input() プロパティは書き込み可能である一方、@Output() プロパティは観測可能です。
@Input デコレーター : コンポーネントを入れ子に配置
- https://angular.jp/guide/inputs-outputs
- プロパティ定義に@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; }
@Outputデコレーター : 子コンポーネントからイベントを受け取る
サービス
- サービスクラスであることの条件は、@Injectable デコレータを付与することのみ。
- @Injectable デコレータは、コンポーネントに対してサービスを引き渡せることを意味する。
登録
- モジュールにサービスを登録する。
- コンポーネントにも登録できる(@Componentデコレーターのprovidersパラメータ)。この場合コンポーネントと子コンポーネントのみで利用できる。
import { HogeService } from './hoge.service'; @NgModule({ : providers: [HogeService], : })
依存性注入
使用する
- 使用するためには、コンストラクタに対応する引数を追加するだけ
- 以下の例の場合、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ライブラリ
- 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 を返します
- 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
- https://karma-runner.github.io/2.0/index.html
- karma.conf.js
Firebase
Firestoreの利用
Tips
機能
Chrome拡張機能
Facebook SDK
Hello.js 認証をまとめるJavaScriptライブラリ
文法
Javascriptのグローバルオブジェクトを利用
declare const hoge;
コード
コードからルーティング
import { Router } from '@angular/router'; : constructor(private router: Router) {} : this.router.navigate(['/hoge'])
CSRF
- https://angular.io/api/common/http/HttpClientXsrfModule
- https://angular.io/guide/http
- Angular + HttpClientXsrfModule + Flask で CSRF
CORS対応
参照
Firebase
© 2006 矢木浩人