日曜日だというのに、天候は今一つ。さすがに鬱々してきたので、散歩でもしてきますかね。
さて、またYii2の話題です。先日、変更/追加を行う場合に、ページを遷移しないでGridView上のModalで変更するというのをやりました。 結構苦労させられたので、記憶が薄れないうちにメモを残しておきたいと思います。
modal/Member.phpを編集する場合を記載します。giiで生成されたCRUDがベースです。ここでは変更ですが、追加や表示も同じ方法で可能です。
views/member/index.php
GridViewに「編集」ボタンを追加
GridViewのwidgetの'columns'に以下を記載します。重要なのは以下の点。
- activity-update-linkクラスを目印にする
- Pjaxを使っている場合には、data-pjax => '0' を指定
[ 'class' => 'yii\grid\ActionColumn', 'header' => '編集', 'template' => '{update}', 'buttons' => [ 'update' => function () { return Html::a('<i class="fa fa-pencil-square-o"></i>編集', '#', [ 'class' => 'btn btn-primary activity-update-link', 'data-pjax' => '0' ]); } ] ]
使用するModal
「編集」ボタンのクリック時に表示されるModalを記載します。 ここではbootstrapのmodalを使っています。modalとmodal-bodyにidを付けておきます。
<div class="modal fade" id="update-modal" role="dialog" data-backdrop="static"> <div class="modal-dialog modal-dialog-centered"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">編集</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body" id="update-modal-body"> </div> </div> </div> </div>
modal表示のスクリプト
「編集」ボタンが押されたら、updateのviewをAjaxでmodal-bodyに読み込んだ後でmodalを表示するスクリプトを追加します。
$(document).on('click', '.activity-update-link', function() { $.get( 'update', { id: $(this).closest('tr').data('key'), }, function (data) { $.fn.modal.Constructor.prototype.enforceFocus = function() {}; $('#update-modal-body').html(data); $('#update-modal').modal(); } ); });
更新のスクリプト
modalの更新フォームのbeforeSubmitイベントで、Ajaxで更新するスクリプトを追加します。
注記: submit時のclient validationを行いたいため、beforeSubmitイベントを使います。
$(document).on('beforeSubmit', '#update-member-form', function() { $('body').removeClass('modal-open'); $('.modal-backdrop').remove(); $('#update-modal').modal('hide'); $.post($(this).attr('action'), $(this).serialize(), function(data, status, xhr) { $('#member-grid-view').yiiGridView('applyFilter'); } ); return false; });
更新成功時に「$('#member-grid-view').yiiGridView('applyFilter');」を行うと、GridViewに変更が反映(再読み込み)されます。
controllers/MemberController.php
更新成功時のredirect()を止めるのと、renderをrenderAjaxに変更を行います。
public function actionUpdate($id) { $model = $this->findModel($id); if ($model->load(Yii::$app->request->post()) && $model->save()) { return; } return $this->renderAjax('update', [ 'model' => $model, ]); }
views/member/_form.php
更新のフォームにidを付けます。
<div class="member-form"> <?php $form = ActiveForm::begin(['options' => ['id' => 'update-member-form']]); ?> <?= $form->field($model, 'company')->textInput() ?> <?= $form->field($model, 'username')->textInput() ?> <?= Html::submitButton('保存', ['class' => 'btn btn-success']) ?> <?php ActiveForm::end(); ?> </div>
modalで変更できるようにするための変更点は以上です。
注意点
注意点を記載しておきます。結構落とし穴が多かった。
Ajax validationと同時に使う場合の注意点
member/updateへの要求がAjaxで行われるようになりますので、そのままではvalidation要求と更新要求との区別がつきません。更新($.post)の際に追加でGETのパラメータを与えて区別するなどの工夫が必要です。
modal('hide')時の注意点
bootstrapの問題で、Ajaxで内容を読み込んだmodalをmodal('hide')した場合、modalの背景が残ってしまうことがあります。たまに残るという現象でちょっと厄介。
これを防ぐために、modal('hide')する際に、以下のお約束を実行します。
$('body').removeClass('modal-open'); $('.modal-backdrop').remove();
modal中でSelect2を使う場合の注意点
これもbootstrapの問題で、Select2の選択肢が表示されない場合が、たまに発生します。
これを防ぐには、modalを表示する前に以下を実行すると良いらしい(bootstrap4の場合は_enforceFocus)。 Yii2のvendor/select2/select2/docs/pages/02.troubleshooting/02.common-problems/docs.mdにも同じ記載があります。
// Do this before you initialize any of your modals $.fn.modal.Constructor.prototype.enforceFocus = function() {};
JavaScript (jquery) は、Ajaxで読み込まれる場所には置かない
今回の場合、views/member/_form.phpに登録のスクリプトを置きたくなりますが、そうしてしまうと、何度も同じスクリプトが実行されてしまって、updateが何度も呼ばれるという事態に陥ります。
views/member/index.phpのように一度しか呼ばれない部分においてください。もしかすると別にいい方法があるかもしれません。
う~ん。長い。