[Angular] (mouseover)/(mouseout)イベントで各ボタンの説明を表示

Angularにおけるマウスオーバーイベントについてのメモ

概要

Angularでは(click)イベントと同様に、要素に(mouseover)イベントを定めることで、マウスオーバー時に実行するメソッドを容易に指定できる

  • 書式
    1
    <div (mouseover)="sampleMethod()">

実装例

各ボタンの説明をマウスオーバー時のみ表示する

  • 実現方法

    • 説明を表示したい要素に以下のイベントを設定
      • (mouseover)
      • (mouseout)
    • イベント発生時に状態値を変更
    • ngIfで状態値に応じて説明を表示
  • 実装例

    • [Angular mat-tree] treeの全展開機能機能の実装で実装したボタンのホバー時に説明を表示するように改修
    • (mouseover)時に状態値:showExpandExplanationの値を変更 → ngIfを設定した要素が表示される
      1
      2
      3
      4
      5
      <button mat-icon-button (click)="treeControl.expandAll()" (mouseover)="showExpandExplanation=true" (mouseout)="showExpandExplanation=false"><mat-icon>unfold_more</mat-icon></button><!--全展開ボタン-->
      <div *ngIf="showExpandExplanation">全展開ボタン</div>

      <button mat-icon-button (click)="treeControl.collapseAll()" (mouseover)="showCollapseExplanation=true" (mouseout)="showCollapseExplanation=false"><mat-icon>unfold_less</mat-icon></button><!--全閉ボタン-->
      <div *ngIf="showCollapseExplanation">折り畳みボタン</div>

参考

関連記事

[Angular mat-tree] treeの全展開機能機能の実装

概要

  • 実装したい機能

    • 複数のtreeを一括で展開/折り畳みする機能
  • 実現方法

    • mat-treeにはデフォルトで全展開に利用可能なメソッドが用意されているため、それらを利用する
  • 全展開

    • expandAll() メソッド
  • 全閉

    • collapseAll() メソッド
  • 全展開機能の書式

    1
    tree.treeControl.expandAll();

全展開/全閉ボタンの実装

1
2
3
4
5
<button (click)="tree.treeControl.collapseAll()">collapse All</button>
<button (click)="tree.treeControl.expandAll()">expand All</button>
<mat-tree #tree [dataSource]="dataSource" [treeControl]="treeControl">
...
<mat-tree>
  • mat-iconを利用した例
    1
    2
    <button mat-icon-button (click)="treeControl.expandAll()"><mat-icon>unfold_more</mat-icon></button><!--全展開ボタン-->
    <button mat-icon-button (click)="treeControl.collapseAll()"><mat-icon>unfold_less</mat-icon></button><!--全閉ボタン-->

参考

関連記事

その他

[Angular ReactiveForm x mat-input] 一つの入力欄のエラーメッセージを出し分ける

  • やりたいこと
    • バリデーションで引っかかった内容毎にエラーメッセージを出し分けて、何を直すべきか?をユーザにリアルタイムで教えたい
    • 入力値に不備がある状態で、ユーザにリクエストをさせない、できない仕組みを作る
  • 同一のフォームで複数のバリデーションルール毎にエラーメッセージを切り替えるには、テンプレート駆動フォームではなく”リアクティブフォーム”の利用が必要

Angularのフォーム テンプレート駆動/Reactive(モデル駆動)

  • フォームの種類
    • テンプレート駆動フォーム
      • フォームの検証ルールをテンプレートとなるhtmlファイルに記載
      • 入力コントロール(Ex. <input>)に属性を付けると、それに対応したFormControlオブジェクトやFormGroupオブジェクトが生成される
      • バリデーション
        • 属性で指定(Ex. required)することで、該当するオブジェクトに適応される
      • 仕組み
        • コンポ―ネントから、オブジェクトにアクセス
    • Reactive(モデル駆動)フォーム
      • テンプレート側で
      • ts側に記載
      • メリット
        • 柔軟なバリデーション、入力されたデータの複雑な制御を実現可能
      • デメリット
        • コードが冗長になりがち
      • 使いどころ
        • 同一のフォーム内で複数のバリデーションルール毎にエラーメッセージを切り替えたいケース
          • Ex. 未入力 or 文字数の不足 or 禁止された入力値の型(大文字禁止なのか等)をユーザにリアルタイムで伝えることでUXの向上を図れる
      • 仕組み
        • コンポーネントにあらかじめFormControlオブジェクトやFormGroupオブジェクトを作っておく
        • テンプレートの入力コントロール(Ex. <input> 要素)からそれらのオブジェクトを参照

実装手順

  • sampleというコンポーネントがあるものとする​
    • sample.component.html
    • sample.component.ts

      モジュールの有効化

  1. app.component.module.tsでReactiveFormsモジュールをimport
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // Reactive form
    import { ReactiveFormsModule } from '@angular/forms'
    // テンプレート駆動型と同様にFormsModuleも必要

    ~略~

    importts:[

    FormsModule,
    ReactiveFormsModule // 追加
    ]
  2. 対象のコンポーネントでも必要なモジュールをimport
  • sample.component.ts
    1
    import { FormGroup, FormControl, FormBuilder, Validators } from '@angular/forms'
  • FormBuilder
    • 必須ではないが、これを利用すると短く楽に記述できる

      実装例

  • Formbuilderを利用する事で完結に記述した例
  • ID x Password型のシンプルなフォームは基本以下でよさそう
    • 時間を作れればSchematicsで自動生成できるようにしておきます

      #### sample.component.ts
  • Formbuilderをconstructorで注入
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    // 1. import module
    import { FormGroup, FormControl, FormBuilder, Validators } from '@angular/forms'

    export class SampleComponent implements OnInit{

    // 2.1. FormGroup参照用の変数を宣言
    credentialForm:FormGroup;

    // 2.2. FormControl参照用の変数を宣言
    id = new FormControl("", [
    Validators.required,
    Validators.minLength(20)
    ]);
    pass = new FormControl("", [
    Validators.required,
    Validators.minLength(40)
    ]);

    // 3. Formbuilderの注入 FormBuilderオブジェクトを生成
    constructor(private fb: FormBuilder) {
    // 4. FormBuilderオブジェクトを使ってFormGroupとFormControlを作成
    this.credentialForm = this.fb.group({
    // formControlと初期値、バリデート条件を列挙
    id: this.id,
    pass: this.pass
    })
    }

    // リクエスト実行時に入力値を参照するサンプル
    submit(){
    // 入力値の参照
    id = this.id.value.id;
    pass = this.pass.value.pass;
    this.sampleService(id, pass); // 認証機能を持つServiceに送る
    }
    }
  • 入力値の参照
    • this.formControlname.valueで取得可能になっている
      1
      2
      this.id.value;
      this.pass.value;

      sample.component.html

  • 入力コントロールとfonrControlオブジェクトの連携、バリデーションに応じたエラーメッセージの表示
    • maxlengthはhtml側に設定すべき
      • ts側でも検知はできたが、規定した時数以上入力を不可にすることはできなかった為
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        <!-- 1. formGroupの設定 -->
        <form [FormGroup] = "credentialForm">

        <mat-form-field>
        <mat-label>accessKeyId</mat-label>

        <!-- 2. FormControlの結びつけ -->
        <input matInput
        formControlName="id"
        placeholder="Ex. XXXXX..."
        maxlength="20">

        <!--&&条件を付けないとエラー解消後もメッセージが残ってしまう為注意-->
        <!-- 3.1. エラーメッセージ 空欄,未入力時 -->
        <mat-error *ngIf="id.invalid && id.invalid.required">入力必須</mat-error>
        <!-- 3.2. エラーメッセージ 文字数不足時 -->
        <materror *ngIf="id.invalid && id.errors.minlength">字数不足</mat-error>

        <!-- 3. FormControlの結びつけ -->
        <mat-form-field>
        <mat-label>Password</mat-label>
        <input matInput
        formControlName="pass"
        placeholder="Ex. FdOI0..."
        maxlength="40"
        [type]="hide ? 'password' : 'text'"
        >
        <!---pattern="[a-zA-Z0-9!-/:-@¥[-`{-~]*"--->
        <!--目のアイコンの押下で入力値の表示を切り替え--->
        <button mat-icon-button
        matSuffix
        (click)="hide = !hide"
        [attr.aria-label]="'Hide password'"
        [attr.aria-pressed]="hide"
        >
        <mat-icon>{{hide ? 'visibility_off' : 'visibility'}}</mat-icon>
        </button>
        <mat-error *ngIf="pass.errors.required">入力必須</mat-error>
        <mat-error *ngIf="pass.errors.minlength">字数不足</mat-error>
        </mat-form-field>

        </form>

  • 送信ボタンの無効化
    • 一つでもバリデーションエラーがあればdisabledで無効化
    • formGroup名.invalid
      • フォームグループ内で一つでもバリデーションエラーがあればtrue
        1
        2
        3
        4
        <button 
        (click)="submit()"
        [disabled]="credentialForm.invalid"
        >送信</button>
  • 他のフォームのバリデーションチェックも条件に加える場合
    • ||でor条件を利用する
      1
      2
      3
      4
      <button mat-raised-button
      (click)="submit()"
      [disabled]="credentialForm.invalid || checkValidation"
      >Get Data</button>

備考

その他

[Angular mat-input] passwordの表示/非表示(*)を切り替えるアイコンを実装

やりたいこと: パスワードのマスク(***と出る)を目アイコンでON/OFF

  • type=passwordで入力値が**で表示される

    • 覗き見を防止するための基本的な仕様だが、入力値の確認も可能にしたい

      Passwordフォームイメージ
  • やり方

    • html側
      • type=変数
        • 条件分岐で ‘password’ : ‘text’に切り替わる
      • 変数の初期値はpassword
      • 入力欄に目アイコンのボタンを追加
        • mat-iconのvisibility、visibility_offを使用
        • matSuffixでbuttonを入力欄の右端に
      • 押下時に入力値が見えるようにする
    • ts側
      • 条件分岐用に変数を定義
        1
        hide = true;

        ‐ Angularにおける変数の条件分岐の書き方
    • 以下は変数hideの値がtrueかfalseかで、?以降の:で区切られた値が採用されるという意味
      1
      [type]="hide ? 'password' : 'text'"

      実装例

  • htmlの実装

    • input内のtype属性
      • 変数hideの値による条件分岐でpassword or textに切り替わる
      • つまり、表示/非表示(*)が切り替わる
    • buttonを作成
      • mat-iconで目のアイコン(visibility, visibility_off)に
      • mat-suffixで右端に
      • (click)時にhideの値を変更
        • (click)=”hide = !hide”
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
                <mat-form-field class="mat-input-margin2">
          <mat-label>secretAccessKey</mat-label>
          <input matInput
          id="secretAccessKey"
          skey="secretAccessKey"
          [(ngModel)]="skey"
          minlength="40"
          maxlength="40"
          pattern="[a-zA-Z0-9!-/:-@¥[-`{-~]*"
          placeholder="Ex. FdOI0..."

          [type]="hide ? 'password' : 'text'"

          required
          #secretAccessKey="ngModel"
          >
          <!--目のアイコンの押下で入力値の表示を切り替え--->
          <button mat-icon-button
          matSuffix
          (click)="hide = !hide"
          [attr.aria-label]="'Hide password'"
          [attr.aria-pressed]="hide"
          >
          <mat-icon>{{hide ? 'visibility_off' : 'visibility'}}</mat-icon>
          </button>

          <mat-error>入力必須</mat-error>

          </mat-form-field>
  • 公式のコードサンプル(stackblitz)

参考

​関連記事

その他

[Angular] 複数のcheckboxで一つ以上のチェックを必須とするバリデーション

AngularでWEB APの画面を作る際に、複数のチェックボックスをユーザに選択させることがよくあると思います。その際にcheckboxの未チェックをリアルタイムで検知する仕組みを作ることで、ユーザに余計な画面遷移やストレスを課さずに済み、不正なリクエストも防げます。

Angularにおけるcheckboxのバリデーション方法

A(単一のcheckbox向け) XX.invalidの利用

  • checkbox要素に以下の変更を加える

    • 属性値にrequiredを追加
      • チェックを必須とするバリデーションチェックが行われる
    • #で名前を付ける
      • ”#で付けた名称.invalid”でバリデーションエラーを検知可能になります
      • つまり、checkをしていなければ、以下のように状態値を確認できます
        1
        XXX,invalid=true
  • 送信button

    • 属性値 [disabled]= || XX.invalidで条件を追加する
    • XX.invalid=trueの時=チェックしていない時に、ボタンがdisabled=無効化される
  • 単一のチェックボックスのチェックを必須化するケースはこれで問題無さそう

    • Ex. 契約事項の確認画面

B(複数のチェックボックス向け) checkした項目を取得する配列が空か確認

  • checkboxの値を取得する配列が空であれば、ボタンをdisabledにする
  • 上記では”一つ以上にチェック”の検知が難しい
    • ngForでループで複数のチェックボックスを表示している場合、全てが必須になってしまう
  • 状態値の取り方以外はAと同様

今回は”複数のチェックボックスから最低一つ以上にチェックすること”を強制するため、Bの実装例を以下に示す

実装例

前提

実装

  • tsファイル側で先にbool値用の変数を宣言

    • check無しの時: true
    • check一つ以上の時: false
  • sample.component.ts

    1
    checkValidation = true;

一つ以上のチェックがあれば、上記の値をfalseに変更する仕組みが必要

  • buttonの無効化条件に追加

    • 一つでもtrueがあればdisabled(ボタンが無効化される状態)になる
  • sample.component.html

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <!--バリデーション状態によって無効化されるボタン-->
    <button mat-raised-button
    [disabled]="Id.invalid || passKey.invalid || this.checkValidation">

    <!--ついでにエラーメッセージも用意-->
    <mat-error *ngIf="checkValidation">最低一項目のチェックが必須</mat-error>

    <!--チェック時にtodoLeafItemSelectionToggle(node)という関数が発火する、チェックボックスをループ表示するサンプル-->

    <ngFor...
    <ng-container>
     <mat-checkbox
    [checked]="checklistSelection.isSelected(node)"
    (change)= "todoLeafItemSelectionToggle(node)"
    >{{node.name}}<!--dashboardにnode.nameの値を渡す (click)="onclickResource(node.name)"-->
    <!--click時にnode.nameを選択値として取得 マーキング--->
    </mat-checkbox>
    </ng-container>
    ...
  • 今回は配列(this.checklistSelection.selected)にチェックした値が入るように機能を作っている

    • ”this.checklistSelection.selected.length = 0”の時、つまり一つもチェックしていない時に、buttonを無効化すれば良い
  • 0をboolean値として扱ってくれるか試したがダメ

    • 0ならtrueにする関数が必要
    • check時にその関数を呼び出して値を変更したい
    • 初期値falseの変数を定義して、check時にtrueに変えるだけでOK
  • checkboxのソース

    • check時に値を取るメソッドを呼び出している
      • (change)= “todoLeafItemSelectionToggle(node)”
        1
        2
        3
        4
        5
        6
                 <mat-checkbox
        [checked]="checklistSelection.isSelected(node)"
        (change)= "todoLeafItemSelectionToggle(node)"
        >{{node.name}}<!--dashboardにnode.nameの値を渡す (click)="onclickResource(node.name)"-->
        <!--click時にnode.nameを選択値として取得 マーキング--->
        </mat-checkbox>
  • check時にチェック数に応じてbool値を変更する関数を定義

    • checkする度にchangeCheckboxValidation()を呼び出す
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/** Toggle a leaf to-do item selection. Check all the parents to see if they changed */
todoLeafItemSelectionToggle(node: FlatTreeNode): void {
this.checklistSelection.toggle(node);
this.checkAllParentsSelection(node);
// バリデーション用のbool値を取得する
this.changeCheckboxValidation();
}

// チェックボックスが一つでもチェックされていれば、bool値をfalseに変更するメソッド
changeCheckboxValidation(){
// check数が0出なければ、checkboxバリデーション用のbool値を変更
if (this.checklistSelection.selected.length != 0) {
this.checkValidation = false;
} else { // check数が0になったらtrueに
this.checkValidation = true;
}
}

以上で”最低一つにチェック”というバリデーションを実現できた

参考

Angular Mateerial関連記事

[Angular mat-input] バリデーションまとめ

  • Anuglarのフォームにおけるバリデーションチェックについてのまとめ

  • バリデーションチェックとは?

    • 最近のWEBサービスでよく見受けられる、リアルタイムで入力値をチェックして問題があればエラーを表示してくれる機能などのこと
      • レガシーなWEBサービスでは、半角、全角が間違っていても申請ボタンを押して更新するまで気づくことができず、ユーザがストレスを受けてしまうケースが多くありました
    • 活用例
      • 未入力の項目があれば、申請ボタンを無効化。該当箇所を赤くマーキング
      • 利用可能な型(Ex. 全角、半角、英数)を規定して制限
      • 文字数を制限
        • 悪意を持った攻撃を防ぐ効果があります
      • passwordを***でマスク
      • typeを規定
        • emailの@や郵便番号や電話番号のーが挟まる形式など

こういったバリデーションを用意に実装できるのがAngularの強みの一つ

Angularのmat-inputにおけるバリデーション

Angularにおいては、input要素に様々な属性値を与えることで、バリデーションチェックを行い、〜〜.valid/~~.invalidといった状態値で確認できる。以下に主なバリデーションパターンと解説を示す。

文字数制限

  • 文字数を強制
    • minlength=””
      • 指定した文字数以下の場合、入力欄がエラー状態になる(赤くなる)
    • maxlength=””
      • 指定した文字数までしか入力できない

必須入力

  • requiredをつけるだけ

typeの指定

  • type=で設定可能
    1
    <input type="email"...

パターンチェック

  • [PatternValidator]というディレクティブを利用することで、正規表現で詳細な条件を定める事ができる

  • 記法:pattern=”条件”

    • 以下は英字(小大)に限定している
      1
      <input name="XXX" ngModel pattern="[a-zA-Z ]*">
  • 英大文字+数字

    1
    <input pattern="[A-Z0-9]*">
  • 英(小,大)+数字(int)+記号

    1
    <input pattern="[a-zA-Z0-9!-/:-@¥[-`{-~]*">
  • ★正規表現を可視化してまとめたチートシート

エラーメッセージの表示

  • エラーメッセージの表示
    • mat-errorを使用
      1
      <mat-error *ngIf="条件"

ngIfで定める条件(バリデーションチェックに合格しているか?)は次項目を使うことが多い。

バリデーション状態の取得

  • テキストボックス(input)に#~~で名前を付ける事で以下の値を条件式で利用可能になる

    • バリデーション成功時:~~.valid
    • バリデーション失敗時:~~.invalid
  • エラーメッセージや最終的な送信ボタンにngIF=”~~.valid”を条件付けすることで、入力値に問題がある状態でのリクエストを未然に防ぎ、UXを向上させることができる

  • 条件が成り立たない時に無効化

    • ngIFではなく[disabled]=条件式を利用する
      1
      <button [disabled]="~~.invalid">ラベル</button>
  • 複数のバリデーションが通っている場合のみ有効化

    • 論理和演算子記号”||”で”または”とする
      1
      <button [disabled]="aaa.invalid||bbb.invalid">
  • 上記では各テキストボックスをひとつずつ条件付けしているが、form要素自体に以下のようにフォーム名を定めれば、全体のバリデータの状態をチェックできる(フォーム名.valid, フォーム名.invalid)

    • 項目が多い場合にはこちらの方が有効
      1
      <form #フォーム名="ngForm">

実装例

IDとパスワードを入力して送信ボタンの押下で認証を行う、基本的なフォームの例を以下に示す。

今回のサンプルにおける条件詳細

  • Id詳細

    • 未入力禁止
      • required
    • 最低文字数
      • minlength=”20”
    • 最大文字数
      • maxlength=”20”
    • 例の表示
      • placeholder
    • patternチェック
      • 英大文字+数字
        1
        pattern="[A-Z0-9]*"
    • エラーメッセージの表示
      • mat-error要素を使用
  • Password詳細

    • 未入力禁止
      • required
    • 最低文字数
      • minlength=”40”
    • 最大文字数
      • maxlength=”40”
    • 例の表示
      • placeholder
    • type
      • passwordで情報を秘匿
    • patternチェック
      • 英小大文字+数字+記号
        1
        pattern="[a-zA-Z0-9!-/:-@¥[-`{-~]*"
    • エラーメッセージの表示
      • mat-error要素を使用
  • Submitボタン

    • 各バリデートの中でエラーが一つでもあればボタンを無効化
      1
      [disabled]="Id.invalid || passKey.invalid"

実装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!--フォームとバリデーション-->

<mat-form-field class="mat-input-margin1">
<mat-label>ID</mat-label>
<input matInput
id="Id"
akey="Id"
[(ngModel)]="akey"
minlength="20"
maxlength="20"
pattern="[A-Z0-9]*"
placeholder="Ex. BSCJJSH7333..."
required
#Id="ngModel"
>
<!--全バリデーションチェック成功時にId.validが成り立つ--->
<mat-error>入力必須</mat-error>
</mat-form-field>

<mat-form-field class="mat-input-margin2">
<mat-label>Password</mat-label>
<input matInput
id="passKey"
skey="passKey"
[(ngModel)]="skey"
minlength="40"
maxlength="40"
pattern="[a-zA-Z0-9!-/:-@¥[-`{-~]*"
placeholder="Ex. Hy2D3..."
type="password"
required
#passKey="ngModel"
>
<mat-error>入力必須</mat-error>
</mat-form-field>

<!--上記のフォームの入力値のバリデーションチェック成功時に有効化される送信ボタン-->

<button mat-raised-button
(click)="submit()"
[disabled]="Id.invalid || passKey.invalid"
>Submit</button>

その他のAngular Materialについては以下にまとめています。

参考

関連記事

その他

[Angular Material入門] mat-inputで生成したフォームから入力値を取得(双方向データバインディング)

Angular初級者向けに、簡単なフォームを生成して入力値を取得するまでを解説します。

基礎知識

前提となる超基本なので分かっている部分は飛ばしてください。

Angular Projectの初め方

  • Angular Project
    • Angularにおけるアプリの雛形/単位のことです
  • Angular PJを生成
    1
    ng new "ap-name"
  • Componentを生成
    • APはコンポーネントと呼ばれる一塊で開発していきます
      1
      ng g component "component-name"
    • コンポーネントは以下のファイルのセットです
      • HTML/Typescript/Scss/karma
    • HTMLが画面、Typescriptが機能、Scssがデザイン、karmaがテスト用ファイルというイメージです

Angular Material

  • Angular Material

    • Googleが提唱しているMaterial DesignというCSSフレームワークを使うためのツール
      • Google/twitter/Microsoft等は、AngularでWEB APを開発しているので、それらのデザインに使われています
    • 要するにAPの画面の部品を、デザイナーでなくとも簡単に作れるようになるツールです
    • 様々なMaterialを組み合わせてパズルのようにUI(画面のこと)を開発していくのがAngular開発の基本です
  • 使用例

  • 設定を覚えれば、上記の様なmaterialをhtmlに1行書くだけで表示できます
    • つまり、これらを順にマスターしていけば、APの画面を自在に作れます

mat-input

  • Angular Material mat Input
    • 今風のフォーム(入力欄のこと)を作れます
    • HTMLに<input mat-input></input>と一行書くだけで、インターネット黎明期のようなフォームにならずに済みます
    • 他にも色々と便利な機能があります
      • オートコンプリート機能
        • 入力途中で予測表示してくれる機能
        • AngularもGoogleが作っているだけあって、ブラウザの記憶機能も使えます
      • プレイスホルダー機能
        • 入力欄に未記入の状態のみ、うっすら案内を表示してくれる機能
        • 記入例をこれで表示するとイケてるAPっぽく見えます
      • バリデーション機能
        • typeの指定
          • 例えば mail にすれば自動的にmailの英数で@を挟む形式以外入力できなくなります
          • 電話番号や郵便番号であれば、自動的に-を補完してくれたりもします
        • 読み込みを挟まないエラー表示
          • 条件を定めると、入力中にリアルタイムでエラー表示等できます
          • 全入力して送信を押すまでエラーが出ないWEBページでイライラしたことありませんか?こういったUX(ユーザ体験)に配慮することでモダンなAPを開発しましょう。例えば、大文字小文字は自動で変換するか先に警告しましょう。

mat-input example

双方向データバインディング

  • 双方向データバインディング
    • html側とts側で変数を自動的に同期することが可能です
  • 記法
    1
    [(ngModel)]="variable-name"

実装

Angular Materialのinstall

  • 以下のコマンド一発で利用の準備が整います
    1
    $your-angular-pj> ng add @angular/material
  • 質問に対しては全てYesでOK
    • 途中でアプリケーションの主要カラーとサブカラーを聞かれます
    • 左が主要カラー/右がサブカラーです

html

  • 実装例を以下に示します
    • input要素にmatInputを属性として書くだけでangular material仕様のフォームになります
    • [(ngModel)]=”key”
      • 双方向データバインディングで、ts側と変数keyを共有しています。入力値が自動的に変数keyに代入されます
    • NaNで変数の値を表示できます
      1
      2
      3
      4
      5
      <mat-form-field>
      <mat-label>KeyId</mat-label>
      <input matInput id="KeyId" key="KeyId" [(ngModel)]="key" min="1" placeholder="Ex. abc..." >
      </mat-form-field>
      {{key}}

ts

  • 変数を宣言するだけでOK
    1
    2
    // 入力値を格納する変数を宣言
    key;

mat-inputにバリデーションを実装する

  • 次に今回作ったフォームに”バリデーションチェック”を実装しましょう

  • バリデーションチェックとは?

    • 最近のWEBサービスでよく見受けられる、リアルタイムで入力値をチェックして問題があればエラーを表示してくれる機能などのこと
      • レガシーなサービスでは、半角全角が間違っていても申請ボタンを押して更新するまで気づくことができず、ユーザがストレスを受けて利用を諦めてしまうことも
    • 活用例
      • 未入力の項目があれば、申請ボタンを無効化。該当箇所を赤くマーキング
      • 利用可能な型(Ex. 全角、半角、英数)を規定して制限
      • 文字数を制限
        • 悪意を持った攻撃を防ぐ効果があります
      • passwordを***でマスク
      • typeを規定
        • emailの@や郵便番号や電話番号のーが挟まる形式など

おまけ:未入力の場合のみ初期値を表示するロジックを開発する

  • ts側に以下の様な判別ロジックを書きます
    • 入力値の有無をif(this.key === undefined)で判断しています
      1
      2
      3
      4
      5
      6
      7
      // 入力値を格納する変数を宣言
      key;

      // 判別ロジック
      if ( this.key === undefined ) {
      this.key = '初期値';
      }

以上です。Angular APのUI(画面)開発はAngular Materialのページで必要そうなMaterialを探して、使い方をググりながら進めていくというイメージを持てればある程度自走できるようになると思います。
Angular Materialについて、公式ドキュメントだけでは難しいものは記事に解説をまとめているので、参考にどうぞ。

参考

関連記事

その他

[Angular] mat-selection-list & ngForでcheckboxをリスト表示~選択値を配列として取得

  • Angular Materialを用いたUIの実装について、初級者向けに以下を解説します
    • チェックボックス型の選択項目をリスト表示
    • 選択値を配列にまとめて格納するロジック
  • 使いどころ
    • 外部サービスやDBに対するリクエストに含めるパラメータを選択値に応じて変化させる
    • 設定ページに良く使います
画面イメージ

1. 基礎知識

1.1. Angular Material

  • Angular Material

    • メニューやツールバー、ボタン、チェックボックスなどの良く使う部品を提供してくれるAngular公式のUIフレームワーク
    • 基本的に画面は0から作らずにMaterialを組み合わせて作ります
    • まだ導入していなれけば以下を実行すると使えるようになります
      • 聞かれることは全部Yesで問題ないです
        1
        ng add @angular/material
  • mat-selection-list

    • checkbox付きのリストを表示するMaterial
    • 今回はこれを使います (詳細は後述)

2. 実装

  • コンポーネント(sample-component)を例に解説します
    • sample-component.html
      • Angular Materialを活用したUI
    • sample-component.ts
      • データの宣言、データ加工機能

2.1. sample-component.html

  • まずAngular Materialを使って画面を作ります

  • 実装例

    • mat-selection-list要素の中にmat-list-option要素を並べます
    • 後でそれぞれ丁寧に解説します
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      <!--選択リスト-->
      <mat-selection-list *ngFor="let item of Data; index as i">
      <ng-container>
      <mat-list-option
      [(selected)]="item.selected"
      [value]="item.value"><!--こっちに書いても発火しない(selectionChange)="checkRegion(i)"--->
      {{item.value}}<!--{{item[i].value}}--->
      </mat-list-option>
      </ng-container>
      </mat-selection-list>

      <br>
      <!--外へリクエストを送るボタン-->
      <button mat-raised-button (click)="submit()">Submit</button>

2.1.1. mat-selection-listとmat-list-optionについて

  • 書式

    • mat-list-optionが選択肢の単位
      1
      2
      3
      4
      5
      6
      7
      8
      9
      <mat-selection-list>

      <mat-list-option
      [(selected)]="item.selected"
      [value]="item.value"><!--こっちに書いても発火しない(selectionChange)="checkRegion(i)"--->
      {{item.value}}<!--{{item[i].value}}--->
      </mat-list-option>

      </mat-selection-list>
  • mat-list-optionの属性値

    • selected
      • checkboxの状態を示す
        • true or false
    • value
      • 要素が持つ値
        1
        2
        3
        <mat-list-option [(selected)]="item.selected" [value]="item.value">
        {{item.value}}
        </mat-list-option>
  • mat-selection-listの属性値

    • (selectionChange)イベント
      • checkされた際に発火するイベント
      • 以下の例では何かが選択される度に”checkRegion()”というメソッドが実行される
        1
        <mat-selection-list (selectionChange)="checkRegion()">
    • mat-list-option側に書いても動かないので注意

2.1.2. *ngFor=”let item of regionsData; index as i”

  • ngFor

    • 構造型ディレクティブと呼ばれるもの
    • ある配列型のデータを元に、ngForを属性値として設定した要素で挟まれた部分をループ表示する
    • 以下をイメージしてください
      1
      2
      3
      <div *ngFor="..<参照するデータを定義(ループの中で使用する)>..">
      <!--ここに書かれた内容をループ表示--->
      </div>
    • HTMLで何行も大量にべた書きする必要がある様な項目を
    • 参照したデータ配列を元に、取り出した変数を変えながら楽に表示できる。すっきりかけるというのがポイント
  • let item of Data;

    • 書式

      1
      let <ループ内での変数名> of <参照するデータ配列>;
    • 意味

      • Data配列をループの中ではitemという名称で扱います
      • 配列から一要素づつ抜き出して、ループ内での変数itemとして使います
    • 使用イメージ

      • tsでデータを定義
        1
        Data =[Apple, Orange, Pinapple]
      • html
        1
        2
        3
        <div *ngFor="let item of Data">
        {{item}} <!--Dataの中身(Apple, Orange, Pinapple)を順に表示-->
        </div>
    • 参照するデータ

      • 上の例で分かるようにts側(sample-component.ts)で配列を定義する必要があります
        • 後で解説します
      • ts側で定義するので外部から取得した動的なデータを元にすることも可能
  • *ngFor=”let item of Data; index as i”

    • Dataに含まれる配列を変数itemとして一つづつ取り出しながらループで表示する
    • 配列のカウントをiで行う
  • ng-container

    • これで囲んだ要素はセットでngForでループする対象にできる
      • 複数の要素を纏めてループする場合にはよく使います
        1
        2
        3
        4
        5
        6
        <div *ngFor="..<参照するデータを定義(ループの中で使用する)>..">
        <ng-container>
        <div> XXXX </div>
        <div> XXXX </div>
        </ng-container>
        </div>

2.1.3. index as i

  • ループが何週目か?をiで定義可能
  • ”0から始まる”ので注意
  • 使用イメージ
    • 実装
      1
      2
      3
      <div *ngFor="let item of Data">
      {{i+1}}番目: {{item}}
      </div>
    • 表示
      1
      2
      3
      1番目: Apple
      2番目: Orange
      3番目: Pinapple

2.2. sample-component.ts

  • ts側には以下を書きます
    • ngForで使うデータソース
    • 選択値を取得するロジック

2.2.1. データソースの準備

  • ngForで参照するデータを以下のように定義します
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 選択肢として表示するregionデータをハッシュで管理
Data = [ // 個々のselectedプロパティでチェック状態を保持
{ value: 'us-east-1', selected: false},
{ value: 'us-east-2', selected: false},
{ value: 'us-west-1', selected: false},
{ value: 'us-west-2', selected: false},
{ value: 'ca-central-1', selected: false},
{ value: 'eu-central-1', selected: false},
{ value: 'eu-west-1', selected: false},
{ value: 'eu-west-2', selected: false},
{ value: 'eu-west-3', selected: false},
{ value: 'eu-north-1', selected: false},
{ value: 'ap-northeast-1', selected: true}, // 初めからcheck=true
{ value: 'ap-northeast-2', selected: false},
{ value: 'ap-northeast-3', selected: false},
{ value: 'ap-southeast-1', selected: false},
{ value: 'ap-southeast-2', selected: false},
{ value: 'ap-south-1', selected: false},
{ value: 'me-south-1', selected: false},
{ value: 'sa-east-1', selected: false},
];
  • 使い方
    • ngForでこの配列から一つづつ呼び出す単位は以下です
      1
      { value: 'us-east-1', selected: false}
    • つまりそのループの中で us-east-1 を示すには以下のように書きます
      1
      2
      3
      <div *ngFor="let item of Data">
      {{item.value}}
      </div>

2.2.2. 双方向バインドによる状態値(selected)の変更

  • これを用いるとパラメータの変化を伝えることができます
  • Angularにおける双方向バインド
    • 片方の変数の変化を
      1
      [(変数)]=変数
  • 今回の使い方
    • ユーザがcheckした際にselectedの値が変わります
    • item.selected=tsで定義したハッシュの該当箇所の選択値が変更されます
      • 後はts側でitem.selectedの値を使って機能を書けます
        1
        2
        <mat-list-option
        [(selected)]="item.selected"

2.2.3. 選択値の取得

  • メソッドを用意して任意のタイミングで呼び出します
    • ハッシュ値全体からfilter()とmap()を使って選択値を取り出します
      1
      2
      3
      4
      5
      6
      7
      8
      9
      // チェックされたリージョンを判別して配列を作成して返す
      makeCheckList() {

      let checkList = [];
      // selectedからチェック状態を確認→配列に選択値を格納
      return checkList = this.Data // Data=ハッシュ値
      .filter((d) => d.selected) // checkされているものに絞る
      .map((d) => d.value); // checkされているリージョン名を取得
      }

2.2.4. 選択値を用いた外部へのリクエスト

  • 外部と通信する機能はServiceに書きましょう

    • Serviceを呼び出す際に、
  • 機能連携イメージ

    • ボタンの(click)イベント

    • (click)=”submit()”と定めていた、submit()メソッドが発火

    • 選択値を取得

    • 外部連携Serviceを呼び出す

      • この際に引数として、取得した選択値を渡す
    • Service内での外部リクエスト

      • この際に引数として渡した選択値を使う
1
2
3
4
5
6
7
8
submit() {

// チェックされたリージョンリストを取得
const REGIONS = this.makeCheckList();

// 外部へリクエストするサービスの呼び出し
let responseData = this.getService(REGIONS);
}

後書き

今回は以上です。このようなロジックを作るシーンは結構あると思います。
短時間でぱっと作って機能面にかける時間を増やしましょう。

関連記事

[Angular Schematics] 開閉可能なサイドナビ&ツールバーを3分で自動生成する

今回はAngular開発におけるComponentの自動生成手法について解説します。
アプリの基本的な画面はこの手法を用いれば、コードを書くまでもなく高速で開発できます。サービスとしての独自性の無い単純作業は極力自動化しましょう。

自動生成したナビゲーション

Angular Schematicsとは?

  • Schematicsの機能

    1. カスタマイズされたComponentをコマンドで自動生成
      • ありがちなUIは大抵これで自動生成できます
        • 今回は例として画面のサイズに応じて自動的に開閉するサイドナビとツールバーでできたナビゲーションを自動生成します
    2. モジュールの追加
    3. 設定の変更
  • 例えば、Angular CLIの以下のコマンドの処理はSchematicsで定義されています

    • ng generate
    • ng add
  • @angular/cdkと@angular/materialに含まれています

  • Angular CLIのng generateやng add といったコマンドの処理はAngular Schematicsによって定義されている

    • 自分で作ることもできます
    • 既存のSchematicsを拡張することも可能
  • 実行コマンド

    1
    ng generate @angular/material:<schematics-name> <component-name>
  • 自作可能

    • ありがちな構成を用意しておけば、サービスを高速で量産できます
    • わざわざ手順書を残すよりも遥かに効率的です

準備

Angular PJを生成

生成済みであればここはスルーしてください

    1. APの雛形を作成
      1
      ng new 'sample-ap'
      • Angular PJの初期画面はこんな感じです
        1
        ng serve --open
        image
    1. Angularの初期画面をまっさらにします
      • src/app/app.component.html をrouter-outletだけを残して削除
        1
        <router-outlet></router-outlet>

Schematicsをinstall

  • ng addコマンドで

  • npm installを使って入れると設定ファイルを弄る必要が出たりします

    1. まずはAngular Materialを入れます
      1
      ng add @angular/material
  • 出力

    • 好きなthemeを選択
      • ひとまずIndigo/Pinkにします
    • 他も基本yesでOKです
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      /firebase-sample (master) $ ng add @angular/material
      Installing packages for tooling via npm.
      Installed packages for tooling via npm.
      ? Choose a prebuilt theme name, or "custom" for a custom theme:
      ❯ Indigo/Pink [ Preview: https://material.angular.io?theme=indigo-pink ]
      Deep Purple/Amber [ Preview: https://material.angular.io?theme=deeppurple-amber ]
      Pink/Blue Grey [ Preview: https://material.angular.io?theme=pink-bluegrey ]
      Purple/Green [ Preview: https://material.angular.io?theme=purple-green ]
      Custom
      ? Set up HammerJS for gesture recognition? Yes
      ? Set up browser animations for Angular Material? Yes
      UPDATE package.json (1437 bytes)
    1. cdkをinstall
      1
      ng add @angular/cdk
  • 出力

    • packege.jsonの中身を自動で改修してくれます
      1
      2
      Skipping installation: Package already installed
      UPDATE package.json (1437 bytes)

画面表示用のComponentを生成

  • top
    1
    \src\app> ng g component top

ナビゲーションを自動生成

  • 実行

    1
    \src\app> ng g @angular/material:navigation
  • 出力

    1
    2
    3
    4
    5
    6
    $ ng generate @angular/material:navigation main-nav
    CREATE src/app/navi/navi.component.scss (193 bytes)
    CREATE src/app/navi/navi.component.html (938 bytes)
    CREATE src/app/navi/navi.component.spec.ts (1234 bytes)
    CREATE src/app/navi/navi.component.ts (583 bytes)
    UPDATE src/app/app.module.ts (1126 bytes)
  • src/appの配下にmain-navコンポーネントが自動生成されています

    1
    2
    3
    4
    5
    6
    7
    8
    src\app\main-nav> ls

    Mode LastWriteTime Length Name
    ---- ------------- ------ ----
    -a---- 2020/04/23 14:59 936 main-nav.component.html
    -a---- 2020/04/23 14:59 193 main-nav.component.scss
    -a---- 2020/04/23 14:59 1256 main-nav.component.spec.ts
    -a---- 2020/04/23 14:59 598 main-nav.component.ts

画面に反映

1. top componentに埋め込む

  • src\app\top\top.component.html
    • app-component_nameで要素を作るだけで入れ子にできます
1
<app-main-nav></app-main-nav> <!--main-naviコンポーネントを参照-->

2. topをルーティングで指定

  • 画面起動時にtopコンポーネントが表示されるように設定

  • src\app\app-routing.module.ts

    1
    2
    3
    4
    5
    6
    //routeを定めるコンポーネントをimport
    import { TopComponent } from './top/top.component';

    const routes: Routes = [
    {path: '', redirectTo: '/top', pathMatch: 'full'}, //デフォルトのパス。AP起動時
    {path: 'top', component: TopComponent }, // top画面のパスを定義

3. Localhostで画面への反映を確認

  • APを起動

    1
    ng serve --open
  • 以下のようにナビが表示されます

    • ツールバーには自動的にAngular-PJ名が入ります
自動生成したナビゲーション
  • サイドナビの表示は画面サイズに応じて変化

      • デフォルトで表示
      • デフォルトでは非表示/バーガーアイコンの押下で開閉
  • Windowを狭めてみる

  • バーガーアイコンを押下
    • Menuが表示されます
  • あとは各画面を生成してサイドナビのMenuから飛べるようにすれば、アプリの雛形ができます

  • この程度の画面はSchematicsを使って3分で実装しましょう

    • 初級者が自力で書くと、ここまででもだいぶ工数を食います

自動生成されるコードの解説

  • Schematicsで自動生成したデフォルト

  • main-nav.component.html

    • angular materialのmat-toolberやmat-sidenaviが使われています
    • WEBアプリだと毎回書きがちなパターン
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      <mat-sidenav-container class="sidenav-container">

      <!--サイドナビ-->
      <mat-sidenav #drawer class="sidenav" fixedInViewport
      [attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'"
      [mode]="(isHandset$ | async) ? 'over' : 'side'"
      [opened]="(isHandset$ | async) === false">
      <mat-toolbar>Menu</mat-toolbar>
      <mat-nav-list> <!--ここをrouterLink="/pass-name"で各画面に飛べるように改修-->
      <a mat-list-item href="#">Link 1</a>
      <a mat-list-item href="#">Link 2</a>
      <a mat-list-item href="#">Link 3</a>
      </mat-nav-list>
      </mat-sidenav>

      <!--ヘッダー-->
      <mat-sidenav-content>
      <mat-toolbar color="primary">
      <!--sidenavの開閉用ボタン-->
      <button
      type="button"
      aria-label="Toggle sidenav"
      mat-icon-button
      (click)="drawer.toggle()"
      *ngIf="isHandset$ | async">
      <mat-icon aria-label="Side nav toggle icon">menu</mat-icon>
      </button>
      <span>scrab-for-aws</span>
      </mat-toolbar>
      <!-- Add Content Here -->
      </mat-sidenav-content>
      </mat-sidenav-container>
  • Tips

    • ヘッダーに複数の要素を表示して、画面サイズに応じて配置を変化させたい場合
    • 要素の間に以下を挟めばレスポンシブな余白ができます
      1
      <span style="flex:auto;"></span><!----余白-->
  • この後の作業イメージ(画面遷移の実装)

    1. 各画面用のコンポーネントを生成
      1
      ng g component <component-name>
    2. app-routing.module.tsでパスを設定
    3. main-nav.component.htmlを改修
      • 各ボタンのrouterLinkに各Componentのパスを設定

参考

@EventEmitter @Input @Output @ViewChild ACM AMP API Gateway AR AR.js AR.js Studio AWS AWS Amplify AWS Budgets AWS Cost Explorer AWS SDK for JavaScript AddThis Adobe XD Alexa Amazon CloudWatch Amazon Honycode Amazon SNS Android Angular Angular Material AngularFire AppSync Augury C CDN CI/CD Cloud Craft Cloud9 CloudFormation CloudFront CloudTrail Cognito Cost Anomaly Detection Cubase ai Cubasis LE DTM Disqus DynamoDB Elgato HD 60S Firebase Firebase Hosting Former2 Github Github Actions Github.com GithubEnterprise GithubPages Google Chrome Google Cloud Shell GraphQL Hexo Hosting IAM Ionic JSON JavaScript LadioCast Logging LowCode MFA MS Authentication MacBook Pro 16 Mind Node NETDUETTO Netflix Party Netlify Network NoCode Observable PO PdM Promise RPA ReactiveForm Recognition Route53 S3 SAM(Serverless Application Model) SAR SSL SYNCROOM Schematics ScrumInc Serverless Service Siri Sitemap Spark AR Steinberg UR44C Teams Touch Cast Studio Treetable TypeScript UI UI Bakery WAF WAFv2 WEB Page Dev WEB会議 WebAR WebSocketAPI Webhook Windows Power Automate Wireshark aot async/await aws config cloud9 display.land draw.io e2e test filter() forkJoin() google search console hexo-generator-amp iOS iPad Pro iPhone icarus map() mat input mat tree mat-checkbox mat-input mat-selection-list mmhmm ngFor plantUML popIn Aladdin2 then() vscode ”global is not defined” らくがきAR アジャイル アジャイル開発 クロスプラットフォーム ショートカット スクラム スクラム開発 テレワーク ファイル操作 ブラウザ型IDE プロダクトオーナー プロダクトマネージャー プロトタイピング リモートセッション 共同開発 双方向データバインディング 待ち合わせ処理 認定スクラムマスター 静的WEB Hosting 静的WEBサイトHosting
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×