Angularでアプリケーションを作るときのメモ
Angularの色々な機能を使う時どんなのだっけなってなるので事前にメモっておく 機能が多いが故に色々なライブラリがあるので基本的には持っているものを使いたい
- 双方向バインディング
- クリックイベント
- DatePipe
- カスタムパイプ
- item$ | async as item
- pipe内で使うやつ
- テンプレートでオブジェクト変数の値を見たい時
- ng-container
- ng-template
- NgModule
- router-outlet
- ルーター上に設定されていないページの指定
- htmlに書いてある#って何?
- ルーティングの遅延読み込み
- アクセス制限をつける(Guard)
双方向バインディング
Vueのv-modelみたいなのはそのままでは使えない
app.module.ts
に FormsModule
を importしてから使う
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; //これ import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, FormsModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
んで、コンポーネントのtsファイルに変数を用意してから html側で下記のようにして使う nameは入力要素をコンポーネント側から識別できるようにするためのものなので必須
<textarea [(ngModel)]="comment" name="name" ></textarea>
クリックイベント
html側で下記のように書く sendMessageは送信用の関数的なので気にしない
<button (click)="sendMessage(comment)" >送信</button>
DatePipe
AngularではテンプレートでDateフォーマット用のAPIが使える またangular/commonにformatDateという関数があるのでts側で使うならそれを使う
:
の後ろに設定されているものはオプション
<small>{{date | date:'yyyy年MM月dd日 HH:mm'}}</small>
const date = Date.now()
表示
2021年10月10日 16:21
フォーマットに関してはドキュメントを参照
複数箇所で使う場合いちいち設定するのは面倒なのでフォーマット部分を .ts
ファイルに記載して
定数化するかカスタムパイプを使って定義する。
カスタムパイプは別途記載
カスタムパイプ
ターミナルでng generateをするファイル名は適当
$ ng g pipe pipes/comment-date
実行するとpipesディレクトリの中に comment-date.pipe.ts
というファイルが作られる
内容は下記の通り
import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'commentDate' }) export class CommentDatePipe implements PipeTransform { transform(value: unknown, ...args: unknown[]): unknown { return null; } }
name:テンプレートから呼び出されるときの文字列 transformメソッド:テンプレートから渡ってくるデータを処理するパイプのメインの処理になる value:テンプレート側渡ってくるデータ args:パイプのオプションが渡ってくる。可変長引数になっているがこれはコロンをいくつも繋げてオプションを複数渡せるから
上のDatePipeでやったフォーマットに合わせて変えると下のような感じになる(transfrm部分だけ記載) formatDateはangular/commonのライブラリ
transform(value: number, ...args: string[]): string { const format = args[0] || 'yyyy年MM月dd日 HH:mm' return formatDate(value, format, 'en-US'); }
template側ではこんな感じ
<small>{{date | commentDate}}</small>
独自のフォーマットを使いたい場合は下記のような指定になる
<small>{{date | commentDate:'MM月dd日'}}</small>
item$ | async as item
基本的に下記のようにitemが取得できた時に表示させるような作りにするが、これだと
item$
は item$.name
のようにドット形式でデータを参照することができない
<div *ngIf="item$ | async"> </div>
この時使うのが as
キーワードを使う
これを使うと解決されたデータをこのテンプレート内に保存できる。
あくまで div 内でのみ使える変数であることに注意 外からだとアクセスできない
<div *ngIf="item$ | async as item"> </div>
pipe内で使うやつ
Observerでpipeは使えるが、pipe内で使うやつは使えないのでimportする必要がある mapだったら
import { map } from 'rxjs/operators';
テンプレートでオブジェクト変数の値を見たい時
普通にテンプレート内でオブジェクトの変数を参照すると
[object Object]
と表示されるので
デバッグする場合は
<div>{{ user | json }}</div>
とすると表示されるようになる (vueだったらpreで囲めば見れるのに・・・・)
ng-container
ダミーのタグとして扱えるangularの組み込みディレクティブ こっちはvueのtemplateと同じ感じで使って良さそう
ng-template
htmlをデータとして保持するための組み込みディレクティブ
データとして保持されるので画面上には出力されない
vueのtemplateと同じ感じで使うというよりも ngIf
の elseとかで使うようにする
NgModule
angularのデコレーターでこれを利用することでモジュールを定義できる
import export を利用してモジュール間で機能を共有できる機能
一番わかりやすいのは app.module.ts
のimportsで設定するFormsModuleで
各モジュールで使えるようにする。
呼び出し先のモジュールでNgModuleをimportsに記述後 呼び出し元でも使えるようにするために呼び出し先のexportsに記載することで使えるようにできる
app.module.ts以外は作成すると CommonModule
がimportされているが
これがないとmoduleとして機能しないので消さないようにする(app-routing.moduleは不要なので消した方が良い)
NgModuleのベストプラクティス
モジュールのスタイルガイドでは下記構成がベストプラクティスとされている
app.module以外の作成は ng g module shared みたいな感じで行うので app/shared/
といった感じで作成されているかも
使うときは忘れずにapp.module.tsにimportする
また、コンポーネントを作成した際使用したいモジュールを設定したい場合は
ng g module header --module=app や ng g module header --module=share
とように設定すると指定することができる (デフォルトは作成したところから一番近いモジュールが選ばれる)
RootModule:アプリ全体のモジュール(app.module.ts) SharedModule:共通モジュール →全てのモジュールで利用する共通化されたコンポーネントやパイプ、カスタムディレクティブを含むモジュール 各モジュールからimportされて使われる app.moduleに記載
FeatureModule:機能モジュール(画面単位) →括弧の通り画面単位で使うものをまとめたモジュール このモジュールに関してはapp.moduleではなくルーティングとセットで行う
CoreModule:一度だけ読み込むモジュール(バージョン7以降でスタイルガイドから削除) →serviceやcomponentやmockデータなど一度だけ読み込みたい時に使う 上のものは基本的にこの中にまとめておくとわかりやすい
router-outlet
これを、 <router-outlet></router-outlet>
のように書くと
routingしたページが表示される
ルーター上に設定されていないページの指定
app-router.module.tsで
const routes: Routes = [ {path: '**', component:NotFoundComponent} ]
のように '**' で記載すると当てはまらない全ての画面で表示できる
htmlに書いてある#って何?
あれはフォーム変数で個別に名前を付けることができるで
テンプレート変数という
ngForm
の値を送る時に使ったり ngIf
のelseに飛ばすときの名前とかで使える
ルーティングの遅延読み込み
機能別にルーティングを分けたいときなどに使う 遅延読み込みの利点はダイナミックインポートを利用することで遷移してから 読み込まれるので表示が早くなる
実装方法
まずルーティングモジュールを作成
--routing
で users-routing.module.tsの作成を行ってくれる
ng g module users --routing
次に画面コンポーネント作成 先ほどできたusersディレクトリに入れる
ng g component users/new-user
次に users-routing.mosule.ts
の設定を行う
routes
に先ほど作成した NewUserComponentを指定
const routes: Routes = [ { path: 'new', component:NewUserComponent} ];
最後に app-routing.module.ts
に今回作成したルーティングモジュールをダイナミックインポートする
routes
定数に下記を記載
const routes: Routes = [ {path: '', component: ChatComponent}, {path: 'users', loadChildren: ()=> import('./users/users.module').then(m=>m.UsersModule)}//遅延読み込み ]
アクセス制限をつける(Guard)
Guardはルーターモジュールのルート設定配列に
canActivate
などを設定することで実現できる
設定方法
ターミナルで設定したいguardの名前を入力
ng g guard core/guards/auth
下のように聞かれるので今回はCanActivateにチェックを入れエンター スペースで複数選択、iで選択項目の反転ができる
? Which interfaces would you like to implement? (Press <space> to select, <a> to to ggle all, <i> to invert selection) ❯◉ CanActivate ◯ CanActivateChild ◯ CanDeactivate ◯ CanLoad
すると下記のようなファイル(auth.guard.ts)ができる canActivateのreturnでtrueだとアクセス可、falseだと不可になる boolean以外にの色々返せる
import { Injectable } from '@angular/core'; import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class AuthGuard implements CanActivate { canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { return true; } }
ファイル自体はServiceとほとんど変わらないが、 implements されるのが CanActivate
になっている
次に app-routing.module.ts
で制限をかけたいファイルに先ほど作成したクラスを指定する
今回はsignup,loginに指定。
配列なので複数クラスを指定できるのに注意
const routes: Routes = [ { path: '', component: ChatComponent }, { path: 'users', loadChildren: () => import('./users/users.module').then(m => m.UsersModule) }, { path: 'signup', component: SignUpComponent,canActivate:[AuthGuard] }, { path: 'login', component: LoginComponent,canActivate:[AuthGuard] }, { path: '**', component: NotFoundComponent } ];
次に auth.guard.ts
に移り canActivate
を下記のように設定
canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { return this.afAuth.authState.pipe( map((user:firebase.User)=>{ if(!user){ return true } else{ this.router.navigateByUrl('/') return false } }) ) }
この場合returnにはObservableが帰ってくるがmap内の真偽値を見て遷移させるかとどまらせるかを設定している
UrlTree
上の書き方はUrlTreeを使うともっと簡単に書くことができる
・UrlTreeとは Angularのルーターが扱うpath情報が保存されているオブジェクト UrlTreeを作成して戻り値に指定するとシンプルなコードで実装ができる
UrlTreeオブジェクトはAngularの parseUrl()
メソッドを使うと生成できる
変更するのはmap内のelse部分
else{ // this.router.navigateByUrl('/') // return false return this.router.parseUrl("/") }
Guardの種類
・CanActivate 対象パスへのページ遷移を許可するか ・CanActivateChild 対象パスの小ルートへの遷移を許可するか ・CanDeactivate 他のパスへの遷移を制限するか ・CanLoad loadChildrenでモジュールを読み込めるか ・Resolve 対象パスへの遷移中に実行する中間処理を指定