kintoneアプリの一覧画面をVue3+Quasarを使って表示
今度はVue3+Quasarを使ってkintoneの一覧画面を表示したいと思います。
Vuetify3のロードマップをみると開発が遅れているという感じなのと、Vuetify3だと表示の崩れが大きいのでこちらも作成してみました。
Vue2+Vuetify2やVue3+Vuetify3だけでは不明点が多かったですが、Quasarでも作成することによって何となく作成方法が掴めてきた気がします。
javascriptを使い始めて2年、ようやく初心者を抜け出せた気がします。
1. 利用環境
以下の環境で動作しています。
- kintone … クラウド最新版(2025年2月時点)
- Vue3 … バージョン3.5.13
- Quasar … バージョン2.17.7
試しやすかったのでCDNから読み込む方式を選択しました。
2. 用意するもの
2-1. 一覧画面を作成
アプリ管理 → 一覧 → 「+」マーク から、新しい一覧をカスタマイズ形式で作成します。
本記事では以下のように設定します。
一覧名 | レコード一覧の 表示形式 |
一覧を表示する範囲 | ページネーションを 表示する |
---|---|---|---|
Vue3+Quasar一覧 | カスタマイズ | PC版のみで表示する | チェックオフ |
2-2. テストデータ
テストデータはこんな感じです。
既にQuasarのリセットCSSでどんな影響が出ているのかわかりますね笑
3. スクリプト全体
一覧に設定するHTMLと、JavaScript/CSSカスタマイズに設置するスクリプトを記載します。
3-1. HTML
<div id="kintone-vue3-app">
<q-layout>
<q-page-container class="q-pa-md">
<q-table
:title="tableTitle"
:rows="rows"
:columns="columns"
:loading="isLoading"
separator="cell"
row-key="$id.value"
>
<template v-slot:loading>
<q-inner-loading showing color="primary"></q-inner-loading>
</template>
<template v-slot:top="props">
<q-space></q-space>
<q-btn
flat round dense
:icon="props.inFullscreen ? 'fullscreen_exit' : 'fullscreen'"
@click="props.toggleFullscreen"
class="q-ml-md"
></q-btn>
</template>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width></q-th>
<q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
>
{{ col.label }}
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props">
<q-td>
<q-btn
unelevated
round
color="primary"
@click="showDetail(props.row)"
>
<q-icon name="description"></q-icon>
</q-btn>
</q-td>
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
>
{{ col.value }}
</q-td>
</q-tr>
</template>
</q-table>
</q-page-container>
</q-layout>
</div>
さきほど作成した「Vue3+Quasar一覧」のHTMLフィールドに本内容をコピペ。
3-2. JavaScript
次に「customizeView.js」という名称で以下の内容を保存します。
((Vue,Quasar) => {
'use strict'
const targetViewId = 5536090
const events = {
index :[
'app.record.index.show',
'mobile.app.record.index.show',
]
}
let vObj = null;
async function getRecords(appId) {
const client = new KintoneRestAPIClient();
const params = {
app: appId,
}
return await client.record.getAllRecords(params)
.then((resp) => {
resp.forEach((item) => {
item.hogehoge = 'テストデータですよー!'
});
return resp
})
}
function generateView(appId) {
const app = Vue.createApp({
setup (props, context) {
const $q = Quasar.useQuasar()
const columnList = [
{ name: 'userName', align: 'center', label: 'ユーザー名', field: row => row.userName.value, sortable: true },
{ name: 'userID', align: 'center', label: 'ユーザーID', field: row => row.userID.value, sortable: true },
{ name: 'mailAddress', align: 'center', label: 'メールアドレス', field: row => row.mailAddress.value, sortable: true },
{ name: 'birthday', align: 'center', label: '生年月日', field: row => row.birthday.value, sortable: true },
{ name: 'hogehoge', align: 'center', label: 'ふがふが', field: row => row.hogehoge, sortable: true },
]
const users = Vue.ref([]);
function addUser(value) {
users.value.push(value)
}
function addUsers(value) {
users.value = value
}
const loadingValue = Vue.ref(true);
function changeLoadingState(value) {
loadingValue.value = value
}
function showDetail(item) {
const url = `/k/${appId}/show#record=${item.$id.value}`
window.open(url, '_blank')
}
function toggleFullScreen (e) {
const target = e.target.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode
$q.fullscreen.toggle(target)
.then(() => {
console.info('fullscreen button push')
})
.catch((err) => {
alert(err)
})
}
Vue.onMounted(() => {
console.info($q)
console.info($q.platform)
})
return {
tableTitle: '利用者一覧',
columns: columnList,
rows: users,
addRow: addUser,
addRows: addUsers,
isLoading: loadingValue,
changeLoadingState,
initialPagination: Vue.ref({
rowsPerPage: 30
}),
showDetail,
toggleFullScreen,
}
},
})
Quasar.Lang.set(Quasar.Lang.ja)
app.use(Quasar, {
config: {
lang: 'ja',
dark: 'auto',
},
})
return app.mount('#kintone-vue3-app')
}
kintone.events.on(events.index, async (e) => {
if ( e.viewType!=='custom' || e.viewId!==targetViewId ) {
return e;
};
vObj = generateView(e.appId)
await getRecords(e.appId).then((resp) => {
vObj.addRows(resp)
})
vObj.changeLoadingState(false);
return e;
});
})(Vue,Quasar);
4. 説明
4-1. スクリプトを動作させる一覧画面を指定する
if (event.viewType!=='custom' || event.viewId!==5536090) {return event;};
アプリ内のいろんなページで動作してもらっては困るので、しょっぱなで一覧IDを指定しておきます。
この箇所にあるevent.viewId
は環境ごとにかわりますので、5536090
の箇所を、前段階で作成した一覧画面に記載の一覧IDに書き換えてください!
4-2. Vueオブジェクト生成時のポイント
setup() {}
内に設定しているレコード一覧users
等をref()
でラップして設定
const app = Vue.createApp({
setup (props, context) {
const $q = Quasar.useQuasar()
const columnList = [
{ name: 'userName', align: 'center', label: 'ユーザー名', field: row => row.userName.value, sortable: true },
{ name: 'userID', align: 'center', label: 'ユーザーID', field: row => row.userID.value, sortable: true },
{ name: 'mailAddress', align: 'center', label: 'メールアドレス', field: row => row.mailAddress.value, sortable: true },
{ name: 'birthday', align: 'center', label: '生年月日', field: row => row.birthday.value, sortable: true },
{ name: 'hogehoge', align: 'center', label: 'ふがふが', field: row => row.hogehoge, sortable: true },
]
const users = Vue.ref([]);
function addUser(value) {
users.value.push(value)
}
function addUsers(value) {
users.value = value
}
const loadingValue = Vue.ref(true);
function changeLoadingState(value) {
loadingValue.value = value
}
function showDetail(item) {
const url = `/k/${appId}/show#record=${item.$id.value}`
window.open(url, '_blank')
}
function toggleFullScreen (e) {
const target = e.target.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode
$q.fullscreen.toggle(target)
.then(() => {
console.info('fullscreen button push')
})
.catch((err) => {
alert(err)
})
}
Vue.onMounted(() => {
console.info($q)
console.info($q.platform)
})
return {
tableTitle: '利用者一覧',
columns: columnList,
rows: users,
addRow: addUser,
addRows: addUsers,
isLoading: loadingValue,
changeLoadingState,
initialPagination: Vue.ref({
rowsPerPage: 30
}),
showDetail,
toggleFullScreen,
}
},
})
<q-data-table>
のデータ本体としたいrows
は初期段階で空配列としています。
対象データが大量な場合は表示までにレスポンスがなく、ユーザビリティが低下するのを防ぐ狙いがあります。
BooleanのisLoading
を用意してロード中という事を伝えているのも同じ理由です。
なお
rows
はHTML内の:items="rows"
と、
isLoading
はHTML内の:loading="isLoading"
と、
それぞれ関連付けています。
vue3ではcomposition API
という仕組みが使えるようになっていて、
const loadingValue = Vue.ref(true);
function changeLoadingState(value) {
loadingValue.value = value
}
といった、"値"と、"それに対して行いたい処理" をまとめた記述ができというのが大きなメリットになっています。
vue2ではmethods
内やcomputed
内に分散させなければならなかったので助かります。
現在はパッと見で全容を把握・整理できる分量の開発しかしていないのでメリットを感じづらいですが、大規模な開発になるにつれこのメリットが活きてくるのではと思います。
タイトル行を設定
const columnList = [
{
name: 'userName', //キー項目
align: 'center', //セル内での横方向表示位置
label: 'ユーザー名', //表示名
field: row => row.userName.value, //kintoneレコードのどの値を表示するか
sortable: true, //タイトル行クリックでの並べ替えを使うか
},
{ name: 'userID', align: 'center', label: 'ユーザーID', field: row => row.userID.value, sortable: true },
{ name: 'mailAddress', align: 'center', label: 'メールアドレス', field: row => row.mailAddress.value, sortable: true },
{ name: 'birthday', align: 'center', label: '生年月日', field: row => row.birthday.value, sortable: true },
{ name: 'hogehoge', align: 'center', label: 'ふがふが', field: row => row.hogehoge, sortable: true },
]
// ~
return {
columns: columnList,
}
columns
を用意し、<q-data-table>
のタイトル行として設定します。
HTML内の:columns="columns"
でこのオブジェクトと関連付けています。
key
に記述する内容は、kintoneのフィールドコード+「.value」といつも使っている形式で変更なし、getRecords()
した内容をそのまま使うわけではない場合は、最後の「ふがふが」列のようになります。
フルスクリーンできるボタンを設置
const $q = Quasar.useQuasar()
// ~
function toggleFullScreen (e) {
const target = e.target.parentNode.parentNode
$q.fullscreen.toggle(target)
.then(() => {
console.info('fullscreen button push')
})
.catch((err) => {
alert(err)
})
}
return {
toggleFullScreen,
}
サンプルページにフルスクリーンの設定があったので追加しました。
肝心のQuasarを、プラグインとして使うよう設定
Quasar.Lang.set(Quasar.Lang.ja)
app.use(Quasar, {
config: {
lang: 'ja',
dark: 'auto',
},
})
QuasarをVue3のプラグインとして設定します。
- 日本語利用
- ダークモードの利用は
auto
を設定することで、ブラウザの状態に合わせて切り替えてくれるようになります。
固定で通常のモードにしたい場合はdark: false,
を、
ダークモードにしたい場合はdark: true,
にしても大丈夫です。
マウント
return app.mount('#kintone-vue3-app')
ここはVuetify3を使う場合と同じです。
↑ここまでの処理が一通り終わったら…
await getRecords(e.appId).then((resp) => {
vObj.addRows(resp)
})
vObj.changeLoadingState(false);
最後にレコード取得&設定と、ローディングバー非表示して完了です。
それぞれsetup()
内で作成した関数addRows()
とchangeLoadingState()
を介して、リアクティブなオブジェクトの値を更新しています。
5. アプリにライブラリ等を反映
アプリ管理 → 設定 → カスタマイズ/サービス連携 → JavaScript/CSSでカスタマイズ
の中に、上から順に以下内容を追加します。
- PC用のJavaScriptファイル
- (URL指定) https://js.cybozu.com/vuejs/v3.5.13/vue.global.prod.js
- (URL指定) https://cdn.jsdelivr.net/npm/quasar@2.17.7/dist/quasar.umd.prod.js
- (URL指定) https://cdn.jsdelivr.net/npm/quasar@2.17.7/dist/lang/ja.umd.prod.js
- (URL指定) https://cdn.jsdelivr.net/npm/quasar@2.17.7/dist/icon-set/material-icons-outlined.umd.prod.js
- (URL指定) https://js.cybozu.com/kintone-rest-api-client/5.7.0/KintoneRestAPIClient.min.js
- (ファイル指定) 先ほど保存した「customizeView.js」
- PC用のCSSファイル
全て指定できたら「保存」→「アプリを更新」します。
6. 完成
ここまでのステップが終了すると、こんな感じになります↓
7. さいごに
アプリタイトルの文字が大きくなりすぎているのが難点ですが、全体的にはVuetify3よりかはkintoneに優しい(笑)ライブラリかなと思いました。
Vuetifyよりも日本語の解説サイトが少なく勉強に難儀していますが、これをスタートラインに色々作成していきたいです!
Discussion