【Vue.js】ページネーションしたらクエリパラメータをつけて同ページに再アクセス可能にする

ページ遷移を伴わない、jsで作成した一覧で、ページ送りしたときクエリパラメータ?page=nをつけたほうが良いというお話です。ブックマークなどから再アクセス可能で便利ですし、リロードやブラウザバックで1ページ目にリセットされてしまうような問題も発生しなくなります。ページネーションだけでなく、検索機能がある場合も同様です。

最近SPAをよく作るのですが、違う情報を表示しているならURLパスも変える、というUI上当然のことを忘れがちになっているので…。

ということでvue.jsでそのような実装サンプルを作成しました。routerは利用しない例です。

要件

  • 一覧ページと詳細ページがある
  • 一覧のリンクをクリックすると詳細ページに遷移する
  • 一覧はvue.jsで作成
  • ページを切り替えると、一覧部分のみjsで書き換える
  • データは1ページずつDBからロードする

デモ

URLに注目してみてみてください。


⬇実際に動作するデモはこちら。

vue pagenation

⬇ソースコードはこちら。

GitHub - ayubntn/vue_pagenation
Contribute to ayubntn/vue_pagenation development by creating an account on GitHub.

実装方法

ページを切り替えたときに?page=nというクエリパラメータを付加し、historyオブジェクトにクエリパラメータも含めたURLを保存します。?page=nつきで再アクセスされた場合は、もちろん該当ページを取得して表示します。
vue.jsで実装していますが、この方法はプレーンなjsでもjQueryでも応用できると思います。
たとえば2ページ目をブックマークして再アクセスも可能になるのでオススメです。

実装方法

一覧とページネーションの、HTMLの実装です。

<div id="vueList" :class="searching ? 'loading' : ''">
	<table class="osare4-table">
		<thead>
			<tr>
				<th style="width: 100px">NO</th>
				<th style="width: 200px">NAME</th>
				<th style="width: 200px">ENG</th>
			</tr>
		</thead>
		<tr v-for="country in countries">
			<td>{{country.id}}</td>
			<td><a href="detail.html">{{country.name}}</a></td>
			<td>{{country.eng}}</td>
		</tr>
	</table>
	<ol class="pagenation">
		<li @click="setPage(1)"><i class="fas fa-angle-double-left"></i></li>
		<li @click="setPage(page - 1)"><i class="fas fa-angle-left"></i></li>
		<li v-for="n of totalPage" :key="n" @click="setPage(n)" class="pagenation_num" :class="n === page ? 'active' : ''">{{n}}</li>
		<li @click="setPage(page + 1)"><i class="fas fa-angle-right"></i></li>
		<li @click="setPage(totalPage)"><i class="fas fa-angle-double-right"></i></li>
	</ol>
</div>

vue.jsの実装です。

const PAGE_PER = 10;

new Vue({
	el: "#vueList",
	data: {
		countries: [],
		page: 1,
		totalPage: 10,
		searching: true,
	},
	mounted() {
		const page = getParam("page");
		if (page != null && !isNaN(page)) {
			this.page = Number(page);
		}
		this.fetch();
	},
	watch: {
		page() {
			const url = new URL(location);
			if (this.page > 1) {
				url.searchParams.set("page", this.page);
			} else {
				url.searchParams.delete("page");
			}
			history.replaceState("", "", url.toString());
			this.fetch();
		},
	},
	methods: {
		fetch() {
			// ここでajaxでバックエンドからデータを取得した方が良い
			this.searching = true;
			setTimeout(() => {
				let start = PAGE_PER * (this.page - 1);
				this.countries = countries.slice(start, PAGE_PER * this.page);
				this.searching = false;
			}, 1000);
		},
		setPage(page) {
			this.page = page;
		},
	},
});

function getParam(name, url) {
	if (!url) url = window.location.href;
	name = name.replace(/[\[\]]/g, "\\$&");
	var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
		results = regex.exec(url);
	if (!results) return null;
	if (!results[2]) return "";
	return decodeURIComponent(results[2].replace(/\+/g, " "));
}

ページ切替時の処理

ポイントとなるのは、vue.jsのwatchの部分。これはページネーションでページを切り替えたときに呼び出される処理です。

watch: {
    page() {
        const url = new URL(location);
        if (this.page > 1) {
            url.searchParams.set("page", this.page);
        } else {
            url.searchParams.delete("page");
        }
        history.replaceState("", "", url.toString());
        this.fetch();
    },
},

const url = new URL(location);
⬆URLというオブジェクトを生成しています。URLをいろいろ操作できるオブジェクトです。

if (this.page > 1) {
    url.searchParams.set("page", this.page);
} else {
    url.searchParams.delete("page");
}

⬆URLオブジェクトのsearchParamsはクエリパラメータを操作することができます。1ページ目を超える場合は?page=nをURLに付与し、1ページ目の場合は?page=nをURLから削除しています。

history.replaceState("", "", url.toString());
⬆ここが最重要。historyオブジェクトにURLを保存しています。こうすることで、2ページ目を表示して、詳細ページに遷移し、ブラウザバックした場合でも?page=2に戻ってこれるようになります。

this.fetch();
⬆これは一覧を再取得するメソッドを呼び出しています。この部分の詳細を続けて説明していきます。

一覧を取得する処理

デモなのでこのコードは参考にならないですが、現在ページのデータをバックエンドから取得する処理を記述します。ajaxで実装が必要です。

※このデモではjs配列の表示する部分を切り出しているだけです。このような実装だと最初に全件ロードが必要なのでデータが大量にある場合に不向きです。

fetch() {
  // ここでajaxでバックエンドからデータを取得した方が良い
    this.searching = true;
    setTimeout(() => {
        let start = PAGE_PER * (this.page - 1);
        this.countries = countries.slice(start, PAGE_PER * this.page);
        this.searching = false;
    }, 1000);
},

this.searching = true;
⬆これはローディングを表示するための処理です。ローディングは、ユーザーインタフェースを考慮すると実装するべきでしょう。

Nメージ目でアクセスされたときの初期ロード処理

vue.jsのmountedは画面初期表示時、DOM構築後に呼び出される処理です。

mounted() {
    const page = getParam("page");
    if (page != null && !isNaN(page)) {
        this.page = Number(page);
    }
    this.fetch();
},

const page = getParam("page");
⬆クエリパラメータの?page=nのnを取得するコードです。getParam関数は別途定義しています。コード全体の方をご確認ください。

if (page != null && !isNaN(page)) {
	this.page = Number(page);
}

⬆クエリパラメータの?page=nが設定されていて、nが数字の場合に、カレントページを切り替えています。もしもクエリパラメータに?page=nがない場合は1ページ目を表示します。また、nにaなど数字でない文字が設定された場合も1ページ目になります。

this.fetch();
⬆これは上記で説明した一覧を再取得するコードです。

その他の実装方法

vue.jsの場合、routerが使えるのなら使わない手はありません!
ブラウザバックしてもページネーションを維持させる方法 | カバの樹

こちらはjquery.pajinateを利用した例で、本記事で紹介しているのと似た方法です。
JSで切り替えたページにBackで戻れるようにする – Qiita

タイトルとURLをコピーしました