日曜日だというのに、天候は今一つ。さすがに鬱々してきたので、散歩でもしてきますかね。
さて、また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のように一度しか呼ばれない部分においてください。もしかすると別にいい方法があるかもしれません。
う~ん。長い。