🚀

【Laravel6】Bootstrapで横並びにしたラジオボタンのバリデーションメッセージを表示する時の落とし穴

2021/02/22に公開

概要

Laravelアプリでコンテンツ登録時にBootstrapで作成したラジオボタンのバリデーションメッセージが表示されない事態に陥りました。

本記事ではその解決方法の一例を紹介します。

※Laravelアプリで発生しましたが、原因はBootstrapだったのでLaravel特有なものではないです。

環境

$ composer -V
Composer version 1.10.20 2021-01-27 15:41:06

$ php artisan --version
Laravel Framework 6.20.16

前提

バリデーションメッセージは各入力項目の下に分けて表示させる。
(「カテゴリーは必ず指定してください。」がバリデーションメッセージです)

よくLaravel教材で採用されているのは、画面上部に全てのバリデーションメッセージを箇条書きで表示する方法で以下のコード。
(公式ドキュメントより)

<!-- /resources/views/post/create.blade.php -->

<h1>ポスト作成</h1>

@if ($errors->any())
    <div class="alert alert-danger">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

<!-- ポスト作成フォーム -->

今回はこちらのコード(例)を採用します。
(こちらも公式ドキュメントより)

<!-- /resources/views/post/create.blade.php -->

<label for="title">Post Title</label>

<input id="title" type="text" class="@error('title') is-invalid @enderror">

@error('title')
    <div class="alert alert-danger">{{ $message }}</div>
@enderror

Bootstrapで横並びにしたラジオボタンのバリデーションメッセージを表示する時の落とし穴の解決方法

解決済みのコード

create.balde.php
{{----}}

<div class="form-group mb-4">
    <p for="exampleInputPassword1">カテゴリー<span class="text-danger">()</span></p>
    {{-- ラジオボタン  --}}
    <div class="form-check form-check-inline is-invalid">
      <input class="form-check-input form-check-inline is-invalid " type="radio" name="category_id" id="category1" value="1" >
         <label class="form-check-label" for="category1">Laravel</label>
    </div>                        
    <div class="form-check form-check-inline is-invalid">
      <input class="form-check-input form-check-inline is-invalid " type="radio" name="category_id" id="category2" value="2" >
      <label class="form-check-label" for="category2">PHP</label>
    </div>                        
    <div class="form-check form-check-inline is-invalid">
      <input class="form-check-input form-check-inline is-invalid" type="radio" name="category_id" id="category3" value="3" >
      <label class="form-check-label" for="category3">Docker</label>
    </div>                        
    <div class="form-check form-check-inline is-invalid">
      <input class="form-check-input form-check-inline is-invalid" type="radio" name="category_id" id="category4" value="4" >
       <label class="form-check-label" for="category4">web基礎</label>
    </div>
    {{-- バリデーションメッセージ --}}
    <span class="invalid-feedback" role="alert">
       <strong>カテゴリーは必ず指定してください。</strong>
    </span>
</div>
{{----}}

落とし穴にハマった時のコード

create.balde.php
{{----}}

<div class="form-group mb-4">
    <p for="exampleInputPassword1">カテゴリー<span class="text-danger">()</span></p>
    {{-- ラジオボタン  --}}
    <div class="form-check form-check-inline">
      <input class="form-check-input form-check-inline is-invalid" type="radio" name="category_id" id="category1" value="1" >
         <label class="form-check-label" for="category1">Laravel</label>
    </div>                        
    <div class="form-check form-check-inline">
      <input class="form-check-input form-check-inline is-invalid" type="radio" name="category_id" id="category2" value="2" >
      <label class="form-check-label" for="category2">PHP</label>
    </div>                        
    <div class="form-check form-check-inline">
      <input class="form-check-input form-check-inline is-invalid" type="radio" name="category_id" id="category3" value="3" >
      <label class="form-check-label" for="category3">Docker</label>
    </div>                        
    <div class="form-check form-check-inline">
      <input class="form-check-input form-check-inline is-invalid" type="radio" name="category_id" id="category4" value="4" >
       <label class="form-check-label" for="category4">web基礎</label>
    </div>
    {{-- バリデーションメッセージ --}}
    <span class="invalid-feedback" role="alert">
       <strong>カテゴリーは必ず指定してください。</strong>
    </span>
</div>
{{----}}

これだとバリデーションメッセージが表示されません。

違いと解決理由

違い

1つ1つのラジオボタン(inputタグのtype=radio)の親のクラスにdivタグにis-invalidがつけているかついていないか

バリデーションメッセージが表示される

<div class="form-check form-check-inline is-invalid">

バリデーションメッセージが表示されない

<div class="form-check form-check-inline>

解決理由

バリデーションエラー時に入力フォームを赤くして、メッセージを表示させるには以下のルールがあったのですが、シンプルにそれを満たしていなかっただけでした。

  • ラジオボタンのinputタグのクラスにis-invalidがついている
  • バリデーションメッセージを表示するDOMの兄弟要素(同じ階層)のタグのクラスにそれぞれis-invalidinvalid-feedbackがついている

こんな感じでinputタグとspanタグを兄弟要素にするとバリデーションメッセージが表示されますが、レイアウトがうまくできませんでした。。

    {{----}}
    <input class="form-check-input form-check-inline is-invalid" type="radio" name="category_id" id="category4" value="4" >
   <label class="form-check-label" for="category4">web基礎</label>
    {{-- バリデーションメッセージ --}}
    <span class="invalid-feedback" role="alert">
       <strong>カテゴリーは必ず指定してください。</strong>
    </span>

補足

実際のコードはLaravelで

  • @errorディレクティブでバリデーションメッセージの有無を判定
  • ラジオボタンの選択肢データをDBにマスターデータとして保持して取得する(カテゴリーの追加を楽にするため)

をしているため実際のコードは以下の通りです。

Viewファイル

create.blade.php
{{----}}
<div class="form-group mb-4">
    <p for="exampleInputPassword1">カテゴリー<span class="text-danger">()</span></p>
    {{-- ラジオボタン  --}}
    @foreach ($categoryForRadioButton as $index => $categoryName)
        <div class="form-check form-check-inline @error('category_id') is-invalid @enderror">
            <input class="form-check-input form-check-inline @error('category_id') is-invalid @enderror" type="radio" name="category_id" id="category{{ $index }}" value="{{ $index }}" {{ old('category_id') == $index ? 'checked': '' }}>
            <label class="form-check-label" for="category{{ $index }}">{{ $categoryName }}</label>
        </div>                        
    @endforeach
    {{-- バリデーションメッセージ --}}
    @error('category_id')
        <span class="invalid-feedback" role="alert">
            <strong>{{ $message }}</strong>
        </span>
    @enderror
</div>
{{----}}

ラジオボタンの選択肢取得用

Category.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Category extends Model
{
    /**
     * ラジオボタン用の配列データを作成
     *
     * @return array
     */
    public function getAll(): array
    {
        $categoryForRadioButton = [];

        $allCategories = $this->all();
        foreach ($allCategories as $index => $category) {
            $categoryForRadioButton[$index + 1] = $category->name;
        }
        return $categoryForRadioButton;
    }
}

参考

Discussion