😼

Windowsアプリ開発エンジニアがWebアプリ開発を頑張ってみた③

2024/04/22に公開

こんにちは。
オアシステクノロジーズの中村です。

前回の記事の続きとなります。

第3段です。

前回は一覧画面のデータを編集画面に表示しデータを更新して一覧に戻ってくる一連の流れを製造しました。

こんなイメージです。

テーブル

編集画面

前回までは一覧から編集画面を表示し一件ずつレコードを更新はできるようになったがファイルに関する処理は未記載のところで今回の始まりです。
https://zenn.dev/oasys/articles/16fbe0e4a4a83c

今日は
一覧に複数のファイルが表示されている時に編集画面に複数のファイルを表示する
編集画面でファイル情報を追加で登録できるようにする
編集画面で複数のファイルを表示し更新する
の製造します

テーブルとダイアログの作りはこんな感じです

foreach
  テーブルの記述
  ダイアログの記述
    form
      ~テーブル項目をダイアログの項目に表示~
      登録ボタン
    /form
endforeach
ダイアログで追加するファイルの記述

一覧に複数のファイルが表示されている時に編集画面に複数のファイルを表示する

まずはファイルが複数登録されているときに表示するソースコードです。(ファイルの箇所だけかいつまんでいます)

@foreach
  <div>
    <div class="row table header">
      <div>ファイル</div>
    </div>
  </div>
  @foreach 一覧のレコード
    <div class="row">
      <div class="cell">
        @foreach 診断書ファイル
          @if($loop->index == 0)
            診断書:
            <br/>
          @endif
          <a href="{{ route('~~url', ファイルID) }}" class="file-link" target="_blank">プレビュー</a>
            <input type="hidden" name="files[file_medical_certificate][{{$loop->index}}][id]"
                value="{{ ファイルID }}"/>
          @if($loop->last)
            <br/>
          @endif
        @endforeach
        @foreach 意見書ファイル
          @if($loop->index == 0)
            意見書:
            <br/>
          @endif
          <a href="{{ route('~~url', ファイルID) }}" class="file-link" target="_blank">プレビュー</a>
            <input type="hidden" name="files[file_medical_certificate][{{$loop->index}}][id]"
                value="{{ ファイルID }}"/>
          @if($loop->last)
            <br/>
          @endif
        @endforeach
        @foreach その他ファイル
          @if($loop->index == 0)
            その他:
            <br/>
          @endif
          <a href="{{ route('~~url', ファイルID) }}" class="file-link" target="_blank">プレビュー</a>
            <input type="hidden" name="files[file_medical_certificate][{{$loop->index}}][id]"
                value="{{ ファイルID }}"/> ←裏で持っており、他にも更新日付等があります
          @if($loop->last)
            <br/>
          @endif
        @endforeach
      </div>
    </div>
  @endforeach
@endforeach

診断書・意見書・その他ファイルの3種類がありそれぞれ登録されていればファイルの列にまとめて表示しています。

次は編集画面でファイルを表示するソースコードです。

<div>
  <span>ファイル</span>
  <div class="invalid-feedback col-12" id="file-error-{{$id}}"></div> ← ファイル全体で1つのエラーとしたいので記載しています
  @foreach レコードで保持しているファイル
    <input type="hidden"
      name="files[{{$id}}][{{$loop->index}}][file_id]"
      value='{{file["id"]}}'> ←ファイルのコンポーネントにもidがあるので裏で持っています、他にも更新日付等あります。
    <x-input-select-enum-box
      id='"files-$id-$loop->index-file_type"'
      class='"file_type-$id"'
      name="files[{{$id}}][{{$loop->index}}][file_type]"
      value='file_type'
      error='$errors->first("$id.$loop->index.file_type")'
      enum="FileType"></x-input-select-enum-box>  ← ファイルの種類で診断書や意見書を区別しています
    <div class="row mb-1">
        <div class="col-9">
            <div class="file-count-{{$id}}">
                <file label="" id="files-{{$id}}-history_file" class='"file_file-$id"'
                    name="files[{{$id}}][{{$loop->index}}][file]"
                    fileName="!empty(!File)?File->file->name:''">
                </file>
            </div>
        </div>
        <div class="col-3">
            @if (!empty($File))
                @if ($File->file_type == FileType::getValue('MedicalCertificate'))
                    <a href="{{ route('cmedicalCertificate.get', $fileMedicalCertificate->id) }}" target="_blank">
                        <button label="ダウンロード" id=""></button>
                    </a>
                    <input type="hidden"
                        name="fileMedicalCertificate[updated_at]"
                        value="{{ $FileMedicalCertificate->file->updated_at }}"/>
                @elseif ($File->file_type == FileType::getValue('Opinion'))
                    <a href="{{ route('opinion.get', $fileOpinion->id) }}" target="_blank">
                        <button label="ダウンロード" id=""></button>
                    </a>
                    <input type="hidden" name="fileOpinion[updated_at]" value="{{ $fileOpinion->file->updated_at }}"/>
                @elseif ($file->file_type == FileType::getValue('Other'))
                    <a href="{{ route('other.get', $fileOther->id) }}" target="_blank">
                        <button label="ダウンロード" id=""></button>
                    </a>
                    <input type="hidden" name="fileOther[updated_at]"
                       value="{{ $fileOther->file->updated_at }}"/>
                @endif
            @endif
        </div>
    </div>
  @endforeach
  <div id="added-file-element-{{$id}}" style="display: none;">
    <x-select-enum-box class="file_type-{{$id}}"
        :id='"new-files-$id-{%index%}-file_type"'
        name="files[{{$id}}][{%index%}][file_type]"
        value=""
        :error='$errors->first("files.$id.$loop->index.file_type")'
        enum="FileType"></x-input-select-enum-box>
    <div class="file-count-{{$id}}">
        <x-input-file label="" class="file_file-{{$id}}"
            id="files-{%index%}-file"
            name="new-files[{{$id}}][{%index%}][file]"
            fileName=""></x-input-file>
    </div>
  </div>
  <div class="row mb-1" id="file-add-{{$id}}">
    <div class="col-9"></div>
    <div class="col-3">
        <x-button-secondary label="+ファイルを追加" id="{{$id}}" class="btn-file-add"></x-button-secondary>
    </div>
  </div>
</div>

今更ですが本来のソースコードはカスタマイズを入れており、変数名等を隠すようにして転記しているので構文誤りは所々あるかと思いますがご了承ください

このソースコードで冒頭に見せたテーブルと編集画面でファイルのみ抜粋した形になります。

ファイルを追加ボタンを押すことでid="added-file-element-{{$id}}"の<div>にファイルに関するコンポーネントを表示させています。

この時[{{$id}}]と[{%index%}]がありますが、それぞれJavaScriptの方で

$idで一覧のどのレコードか

%index%で何番目のファイルか

を設定しています。

こちらはファイルを追加ボタン押下時の処理です。

function addIllnessHistoryFileEvent() {
    let currentBtn = $(this);
    let currentId = $('.file-count-' + currentBtn.attr('id'));
    let id = currentId.length - 1;
    let $clone = $("#added-file-element-" + currentBtn.attr('id')).clone().attr('id', id);
    $clone.html(function (i, html) {
        return html.replace(/{%index%}/g, id);
    });

    // 要素を追加する
    $clone.insertBefore("#file-add-" + currentBtn.attr('id'));
    // 要素を活性化する
    $clone.find("input, select, textarea").prop("disabled", false);

    // 要素を表示する
    $clone.show();
}

これによって

一覧に複数のファイルが表示している時に編集画面に複数のファイルを表示する

編集画面で登録するファイルを追加できる

ができました!!

編集画面でファイル情報を追加で登録できるようにする

データ更新は前回記述したこのソースコードに追記します。

対応①:JavaScriptの記述で更新箇所にファイル情報を追記する

function registerItem(e) {
    e.preventDefault();
    let currentBtn = $(this);
    let $currentId = currentBtn.attr("data-id");
    let $form = $('#form-' + $currentId);
    let $route = "秘密";
    let file_ids = [];
    let file_types = [];
    let file_updated_ats = [];
    let new_file_ids = [];
    let new_file_types = [];
    let new_file_updated_ats = [];
    let index = $('.file-count-' + currentId);
    for (let i = 1; i < index.length; i++) {
        file_types[i - 1] = $form.find('#files-' + i + '-file_type').val();
        let fileId = $form.find('input[name="files[' + i + '][file][file][id]"]');
        if (fileId.length > 0) {
            file_ids[i - 1] = fileId[i - 1].value;
        } else {
            file_ids[i - 1] = null;
        }
        let fileUpdatedAt = $form.find('input[name="files[' + i + '][file][file][updated_at]"]');
        if (fileUpdatedAt.length > 0) {
            file_updated_ats[i - 1] = fileUpdatedAt[i - 1].value;
        } else {
            file_updated_ats[i - 1] = null;
        }

        let newFileId = $form.find('input[name="new-files[' + currentId + '][' + i + '][file][file][id]"]');
        if (newFileId.length > 0) {
            new_file_ids[i] = newFileId[0].value;
        } else {
            new_file_ids[i] = null;
        }
        let newFileType = $form.find('#new-files-' + currentId + '-' + i + '-file_type');
        if (newFileType.length > 0) {
            new_file_types[i] = newFileType[0].value;
        } else {
            new_file_types[i] = null;
        }
        let newFileUpdatedAt = $form.find('input[name="new-files[' + currentId + '][' + i + '][file][file][updated_at]"]');
        if (newFileUpdatedAt.length > 0) {
            new_file_updated_ats[i] = newFileUpdatedAt[0].value;
        } else {
            new_file_updated_ats[i] = null;
        }
    }
    $.ajax({
        url: $route,
        method: 'POST',
        data: {
            _token: $('input[name="_token"]').val(),
            data: {
                id: $form.find('#id-' + currentId).val(),
                name: $form.find('#name-' + currentId).val(),
                birthday: $form.find('#birthday-' + currentId).val(),
                file_types: file_types,
              file_ids: file_ids,
              file_updated_ats: file_updated_ats,
              new_file_ids: new_file_ids,
              new_file_types: new_file_types,
              new_file_updated_ats: new_file_updated_ats,
            },
        },
        dataType: 'json',
        success: function (response) {
            location.reload();
        },
        

これまでのidとindexで、ファイル類(id、種類、更新日付)がどのレコードのどこにあるかを識別可能になっています。

そのためどのレコードかをidで識別し、いくつのファイルが存在するかをカウントしてそのカウント分ループすることで

ファイル情報にアクセスが可能なので、バックエンドに渡すために配列に格納しました。

これでバックエンドに渡して更新が可能となります。

対応②:Laravelのrequestに記載してあるエラーチェックの結果を画面に表示する

更新時はファイルが設定されている&ファイルの種類が条件なのでエラーが発生する場合は画面に表示する必要があります。

success: function (response) {
    location.reload();
    },
    error: function (jqXHR) {
        if (jqXHR.status === 422) {
            let response = JSON.parse(jqXHR.responseText);
            if (response.body) {
                if (response.body["id"]) {
                    let id = $form.find('#id-' + currentId);
                    id.parent().parent().find('.invalid-feedback').text(response.body["id"]);
                }
                if (response.body["name"]) {
                    let name = $form.find('#name-' + currentId);
                    name.parent().parent().find('.invalid-feedback').text(response.body["name"]);
                }
                if (response.body["birthday"]) {
                    let birthday = $form.find('#birthday-' + currentId);
                        birthday.parent().parent().find('.invalid-feedback').text(response.body["birthday"]);
                }
                var pattern1 = /file_types\..*/;
                var pattern2 = /file_ids\..*/;
                var responseBodyString = JSON.stringify(response.body);
                if (pattern1.test(responseBodyString) || pattern2.test(responseBodyString)) {
                    $form.find('#file-error-' + currentId).text('ファイル種別とファイルは合わせて設定してください。');
                }
            }
            return;
        }
    },
}

編集画面でのソースコードを記述したところで「ファイル全体で1つのエラーとしたいので記載しています」と文言があったと思いますが、

ファイルに関するエラーチェックはそこに表示しております。

これによって編集画面で複数のファイル情報を登録することができます。

これにて

Tableタグで一括更新していたデータを編集画面から個別に追加・更新できるようにする

の改修が完了しました。

ソースコード自体もボリュームがあるので全てを書ききれなかったり、部分的に掻い摘んでいるので不完全ではありますが

このような流れで改修を行ったことは分かるかなと思います。

~感想~

できる人からしたらたったこれだけかもしれないですが、かなり時間かかりました。

あまりフロントはしたくないです(苦笑い)

Discussion