5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

YiiAdvent Calendar 2012

Day 7

YiiのPaginationをブラウザの戻るボタンに対応させる

Last updated at Posted at 2012-12-13

CListViewCGridView のPaginationは、デフォルトでAjaxを使うようになっていて、次のページがリンクしているリソースを再ロードしないので、レスポンスも高速だしサーバにも優しいです。
しかも、非常に高度なHTMLとJavaScriptでPaginationまで付けてくれるのに、ビューに書くコードはせいぜいこれぐらいでOKという優れもの。

<?php $this->widget('zii.widgets.CListView', array(
	'dataProvider'=>$dataProvider,
	'itemView'=>'_view',
)); ?>

ところが、これらのウィジェットは、デフォルトではひとつ困ったことがあります。それはブラウザのヒストリに前のページが残らないこと。何かの項目をクリックして詳細を閲覧し、「あ、これじゃない」と思ってとっさに戻るボタンをクリックしたとき、その項目があった状態に戻れないんですね。前の状態がAjaxで変化した結果なので。というわけで、それに対応する方法を少し。

対策1 Ajaxをやめる

<?php $this->widget('zii.widgets.CListView', array(
	'dataProvider'=>$dataProvider,
	'itemView'=>'_view',
	'ajaxUpdate'=>false,  // これ追加
)); ?>

はい、これだけでAjaxをオフにでき、2ページ目以降に行くと、HTML全体がリロードされるようになります。で、URLには、Item_page=2 とかそんな感じのパラメータが付くことになります。

ただしこの方法だと、ページを送るたびに1ページ分まるごとリクエストやり直しになり、せっかくリンクしている jQuery が使われなくてもったいないですね。まあ、これでも普通のページ送りですが、ちょっとWeb2.0フレームワーク感がない。

対策2 pjaxにする

で、次の方法は Yii 1.1.11 からなので最近のプロジェクトでないとダメかもしれませんが、マジですごくいいです。なんとこのバージョンで、CListViewCGridViewenableHistory というステキなプロパティが増えました。

<?php $this->widget('zii.widgets.CListView', array(
	'dataProvider'=>$dataProvider,
	'itemView'=>'_view',
	'enableHistory'=>true,  // これを追加
)); ?>

こうすると、AjaxでDOMを更新しつつ、ブラウザのアドレスバーに履歴を残してくれます。いわゆる pjax というやつです。GitHubのソースを閲覧するときフォルダを掘ったり戻ったりするアレですね。

やってみると、対策1と見た目が同じに見えるかもしれませんが、ブラウザのデバッガでよく調べると、HTML全体がリロードされていないことがわかります。

また、微妙な変化ですが、ページのスクロール位置が維持されていると思います(環境によるので保証はないですが)。この次へボタンが同じ位置を維持するというのは、わりとUIの使いやすさにつながるんですよね。

ただしこれ、ブラウザが pushState をサポートしている必要があります。また、アドレスバーは1つしかないので、1ページ内に複数の CListViewCGridView を置いて両方 pjax したいというのはダメです。

Yii のソースには History.js が含まれてたので、少々古いIEでもなんとか動くようにしてくれるかもしれませんが、そこはちょっと未検証。

(実はこれ書いてる本人が、softark さんに教えてもらうまでこの新プロパティが増えてたことを知りませんでした。YiiのCGridViewでpushStateできたらいいのにって、もう1.1.12がリリースされてるのに言ってた/// どうもありがとうございます。)

pjaxがダメだったときすぐAjax更新をオフにできるように

オススメはpjaxですが、もしユーザの多くがpjaxできない場合、彼らにはデフォルトのAjax更新を強いることになります。そうなったときユーザが「サーバ負荷は気にしないからブラウザの戻るボタンを効かせたい」と言い出したら、すぐにAjax更新を使わないほうの対策に切り替えたいですね。

Yii:app()->widgetFactory コンポーネントを config/main.php で構成して、特定の種類のウィジェットが作られるときのシステム全体でのデフォルト値を指定しておけば、変更スポットを集約しておけます。

protected/config/main.php
<?php

	'components'=>array(
		...
		'widgetFactory'=>array(
			'widgets'=>array(
				'CListView'=>array( // CListViewのデフォルト
					'enableHistory'=>true, // pjax on
					'ajaxUpdate'=>true, // ajax on
				),
				'CGridView'=>array( // CGridViewのデフォルト
					'enableHistory'=>true, // pjax on
					'ajaxUpdate'=>true, // ajax on
				),
				...
			),
		),

これで、config/main.php に登録したウィジェットを使っている箇所では、カスタマイズされた設定を使いつつ、ソース上ではGiiが生成したまんまに近いコードを維持できます。

<?php $this->widget('zii.widgets.CListView', array(
	'dataProvider'=>$dataProvider,
	'itemView'=>'_view',
)); ?>

デフォルトは main.php から引き継ぎ、アドホックに必要なものはその場で指定したものに。

これで、今後Giiで新しいビューを作ったときも、コード変更なしで同じ方針が引き継げて便利ですね。

5
5
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
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?