12
11

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.

Vue.jsとvue-router.jsでpagerを作る

Last updated at Posted at 2017-10-03

SPAを作るような目的ではなく、ページの一部としてvueを用いたページャーがあったら便利そうだなと思ったのと、nodeと一緒にvue.js使う例はあるのですが、サーバーを使わないでhtmlファイルのみですぐ確認できるものを作ってみたかったこともありまして作ってみました。

試行錯誤しながら作りましたので、スマートなコードではないかもしれませんが置いておきます。何かのヒントになれば幸いです。

概要

QiitaのAPIを使用して記事のリストをaxiosで取得して表示してそれにページング機能を追加したものです。

CodePen上とQiita上ではブラウザバックの動作は問題ありませんが、更新時のページ保持が動作しないようです。

See the Pen VueRouterPagenation by horikeso (@horikeso) on CodePen.

確認時の注意

API v2

認証している状態ではユーザごとに1時間に1000回まで、認証していない状態ではIPアドレスごとに1時間に60回までリクエストを受け付けます。

pageの初期値は1、pageの最大値は100に設定されています。また、per_pageの初期値は20、per_pageの最大値は100に設定されています。

ということなのでいじっていると途中で取得できなくなるかもしれません。

page=101から
{"message":"Bad request","type":"bad_request"}

per_page=101から
{"message":"Rate limit exceeded","type":"rate_limit_exceeded"}

すぐ確認できなくなるので個人のアクセストークンで認証しています。

read_qiita - Qiitaからアクセストークンに紐付いたユーザに関連したデータを読み出す

JSON Schema

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>VueRouterPagenation</title>
<script src="https://unpkg.com/vue@2.5.16/dist/vue.min.js"></script>
<script src="https://unpkg.com/vue-router@2.7.0/dist/vue-router.js"></script>
<script src="https://unpkg.com/axios@0.18.0/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
<nav>
<ul>
  <router-link v-if="basePageId > 1" tag="li" :to="{ path: 'page', query: { action: 'pre' + basePageId }}"><a>&#60;</a></router-link>
  <router-link v-if="basePageId <= parseInt(articleCount / pageUnit, 10)" tag="li" :to="{ path: 'page', query: { action: (basePageId).toString() }}"><a v-if="currentPage === basePageId"><span style="color:green;">{{ basePageId }}</span></a><a v-else>{{ basePageId }}</a></router-link>
  <router-link v-if="basePageId + 1 <= parseInt(articleCount / pageUnit, 10)" tag="li" :to="{ path: 'page', query: { action: (basePageId + 1).toString() }}"><a v-if="currentPage === basePageId + 1"><span style="color:green;">{{ basePageId + 1 }}</span></a><a v-else>{{ basePageId + 1 }}</a></router-link>
  <router-link v-if="basePageId + 2 <= parseInt(articleCount / pageUnit, 10)" tag="li" :to="{ path: 'page', query: { action: (basePageId + 2).toString() }}"><a v-if="currentPage === basePageId + 2"><span style="color:green;">{{ basePageId + 2 }}</span></a><a v-else>{{ basePageId + 2 }}</a></router-link>
  <router-link v-if="basePageId + 3 <= parseInt(articleCount / pageUnit, 10)" tag="li" :to="{ path: 'page', query: { action: (basePageId + 3).toString() }}"><a v-if="currentPage === basePageId + 3"><span style="color:green;">{{ basePageId + 3 }}</span></a><a v-else>{{ basePageId + 3 }}</a></router-link>
  <router-link v-if="basePageId + 4 <= parseInt(articleCount / pageUnit, 10)" tag="li" :to="{ path: 'page', query: { action: (basePageId + 4).toString() }}"><a v-if="currentPage === basePageId + 4"><span style="color:green;">{{ basePageId + 4 }}</span></a><a v-else>{{ basePageId + 4 }}</a></router-link>
  <router-link v-if="basePageId + 5 <= parseInt(articleCount / pageUnit, 10)" tag="li" :to="{ path: 'page', query: { action: 'next' + basePageId }}"><a>&#62;</a></router-link>
</ul>
</nav>
<router-view></router-view>
</div>
</body>
</html>
// Components
const Page = { template: '\
    <div v-if="$root.linkList.length">\
        <div v-html="link" v-for="link in $root.linkList">{{ link }}</div>\
    </div>\
    <div v-else-if="$root.errorFlag">リストの取得に失敗しました</div>\
    <div v-else>Loading...</div>'
};

// Router
const router = new VueRouter({
    routes: [
        { path: '/page', component: Page}
    ]
});

document.addEventListener('DOMContentLoaded', function() {
    const app = new Vue({
        el: '#app',
        data: {
            basePageId: 1,
            pagerUnit: 5,
            pageUnit: 25,
            articleCount: null,
            currentPage: 1,
            errorFlag: false,
            pattern: new RegExp("^[0-9]*$"),
            linkList: []
        },
        created () {
            this.articleCount = this.pageUnit * 100;// 100ページまでしか取得できないので
            this.getPage();
        },
        watch: {
            // ルート変更時
            '$route': 'getPage'
        },
        methods: {
            getPage() {

                this.errorFlag = false;// 初期化
                this.linkList = [];// 初期化
                if (typeof this.$route.query.action === "undefined") {
                    location.href = "qoGQRm#/page?action=" + this.basePageId;
                    this.actionCount++;
                    return;
                }

                const currentPageId = this.basePageId;

                if (this.$route.query.action.indexOf("pre") !== -1) {
                    this.$route.query.action = this.$route.query.action.replace(/pre/g, "");
                    if (this.pattern.test(this.$route.query.action) === true && parseInt(this.$route.query.action, 10) > 0) {
                        this.$route.query.action = (parseInt(this.$route.query.action, 10) - this.pagerUnit).toString();
                        this.basePageId = Math.ceil((parseInt(this.$route.query.action, 10) - this.pagerUnit) / this.pagerUnit);
                    } else {
                        this.basePageId = 1;
                        this.$route.query.action = "1";
                    }
                }

                if (this.$route.query.action.indexOf("next") !== -1) {
                    this.$route.query.action = this.$route.query.action.replace(/next/g, "");
                    if (this.pattern.test(this.$route.query.action) === true && parseInt(this.$route.query.action, 10) > 0) {
                        this.$route.query.action = (parseInt(this.$route.query.action, 10) + this.pagerUnit).toString();
                        this.basePageId = Math.ceil((parseInt(this.$route.query.action, 10) + this.pagerUnit) / this.pagerUnit);
                    } else {
                        this.basePageId = 1;
                        this.$route.query.action = "1";
                    }
                }

                if (this.pattern.test(this.$route.query.action) !== true) {
                    this.errorFlag = true;
                    return;
                }

                // 現在位置判定用保持
                this.currentPage = parseInt(this.$route.query.action, 10);

                // ブラウザリロード時に位置を保持する対策
                this.basePageId = parseInt((parseInt(this.$route.query.action, 10) - 1) / this.pagerUnit, 10) * this.pagerUnit + 1;

                axios.get("https://qiita.com/api/v2/items?page=" + this.currentPage + "&per_page=" + this.pageUnit, { 'headers': { 'Authorization': 'Bearer 0db1d5ad3a19263f1e199e78b1d429b173acd2a8' }})
                    .then(result => {
                        const objectList = result.data;
                        let linkList = [];
                        objectList.forEach(function (object) {
                            linkList.push('<a href="' + object.url + '" target="_blank" rel="noopener">' + object.title + '</a>');
                        });
                        this.linkList = linkList;
                    })
                    .catch(error => {
                        this.errorFlag = true;
                    });
            }
        },
        router
    });
});
#app ul {
    list-style-type: none;
    display: flex;
} 
#app li + li {
  margin-left: 10px;
}

参考

https://jp.vuejs.org/v2/guide/index.html
https://router.vuejs.org/ja/essentials/getting-started.html

12
11
2

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
12
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?