LoginSignup
1
1

More than 5 years have passed since last update.

Angular: @ng-select/ng-selectを利用したセレクトボックスのバリデーションチェック

Last updated at Posted at 2019-02-28

はじめに

Angular6 + bootstrap4 でフロント開発中。
普段は、できるだけテンプレート駆動フォームで開発しています。

この度、セレクトボックスで複数選択可能のかっちょいい見た目にしたく、@ng-select/ng-selectを導入し実装した際に、バリデーションチェックのコメント表示でハマったのでそのへん含めてまとめています。
@ng-select/ng-selectの詳しい使い方はこちらを参照。

※全く別のパッケージでng-selectという名前のものもあるので気をつけてください!
image.png

テキストエリア

普段はこんな感じで、requiredといったAngularの標準バリデーションチェック機能を利用しています。
エラー時のメッセージはbootstrapのinvalid-feedbackのデザインを適用して表示しています。

textarea.component.html
<div class="form-group row">
  <label class="col-sm-3 col-form-label" for="userEmail">ユーザメールアドレス</label>
  <div class="col-sm-9">
    <input type="text" class="form-control" name="userEmail" [(ngModel)]="user.user_email" #userEmail="ngModel"
      [ngClass]="{ 'is-invalid': f.submitted && userEmail.invalid }" required email/>
    <div *ngIf="f.submitted && userEmail.invalid" class="invalid-feedback">
      <div *ngIf="userEmail.errors.required">この項目は必須です。</div>
      <div *ngIf="userEmail.errors.email">メールアドレスの形式で入力してください。</div>
    </div>
  </div>
</div>

ng-selectを適用したセレクトボックス

前述の通り、@ng-select/ng-selectを使ってセレクトボックスの機能&見た目を拡張しているのですが、テキストエリアのバリデーションと同じようにできるだろうと思っていたら意外とハマりました。。

まず、完成形です。

selectbox.component.html
<div class="form-group row">
  <label class="col-sm-3 col-form-label" for="account">アカウント選択</label>
  <div class="col-sm-9">
    <ng-select [items]="accounts | async" name="account" bindLabel="name" autofocus bindValue="id"
      [(ngModel)]="user.account_id" required #account="ngModel" (change)="changeAccount($event)"></ng-select>
    <div *ngIf="f.submitted && account.invalid" class="invalid-feedback d-block">
      この項目は必須です。
    </div>
  </div>
</div>
selectbox.component.ts
accounts: Promise<any>;    // ほんとはanyダメよ
public ngOnInit() {
    this.accounts = this.accountService.getData();
}

注意点1

・導入時のハマりポイント!
これはあくまで私の環境だけの可能性もありますので、適宜読み替えてください。

  • angular.jsonにcssのパスを記載しても適用されない

→公式にできるって書いてるのにできませんでした。。
結局、公式のアナウンス通り、styles.scssにimport文を書くと無事適用されました。

  • module.ts を分割していると適用範囲がわかりにくい

こちらは、ng-selectを適用したいcomponentの直近のmodule.tsにng-selectをimportしないとエラー出ちゃいました。

注意点2

・バリデーションチェック時のcssを追記する必要がある!
公式にアナウンスがあったのですが、私が読み飛ばしていました。。

styles.scss
ng-select.ng-invalid.ng-touched .ng-select-container {
    border-color: #dc3545;
    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 0 3px #fde6e8;
}

基本的にng-selectでもrequiredなどのバリデーションチェックパラメータは有効です。
しかし、バリデーションチェックにかかったときに付与されるクラスが、ng-invalidというクラスに変わるのでそこが注意ポイントです。
もちろん、bootstrapの標準ではないので、自分で追記する必要があります。

注意点3

invalid-feedbackが効かない!
注意2のバリデーションチェック時のクラス名が変わることが分かっていればすぐ解決できたのですが、ハマってしまいました。。
bootstrap4によるとinvalid-feedbackは、.is-invalid.is-validに反応してスタイルを 適用する/しない を制御してるみたいです。

ng-selectは、対象となるセレクトボックスのオブジェクトに対して、Angularのinvalidパラメータは付与してくれますが、bootstrapの.is-invalidは付与してくれないようです。

しかし、セレクトボックスのエラーメッセージだけcss書くなんてやりたくないので、調べていると、.d-blockというのを見つけました。
これは、強制的にinvalid-feedbackを有効化してくれるクラスらしく、こいつを使うことで、
div要素の出し入れは通常通りAngularのngIfディレクティブにまかせて、エラーメッセージの適用はbootstrapという形ができました。

該当箇所.html
<div *ngIf="f.submitted && account.invalid" class="invalid-feedback d-block">
  この項目は必須です。
</div>

さいごに

セレクトボックスにこだわったあまり、@ng-select/ng-selectと戦ったよって話でした。
サードパーティ製のフォーム系パッケージを使うときは、いろんなものとバッティングすることを考えて技術選択しないとだな、と反省しました。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1