Cybozu CDNにも公開されているようにVue.jsのJavaScriptライブラリーが近年シングルページアプリケーション(SPA)の開発環境として人気を博しているようです。
AngularJSやReactよりも比較的に覚えやすく、フロントエンド初心者でもとっつき易いプラットフォームだと思います。
今回は、このVue.jsのライブラリーとVue.js用のUIライブラリーのVuetify.jsを使って、カスタマイズビュー上でレコード一覧と詳細をシングルページ上で実現します。
リアクティブな検索機能も作成します。
STEP1:kintoneアプリの設定・変更
固定リンクがコピーされました
kintoneアプリストアにて、検索テキストボックスに「顧客リスト」と入力し検索します。
そして、検索結果の「顧客リスト」アプリを追加します。
フィールドコードの変更
固定リンクがコピーされました
「顧客リスト」アプリを開き、「アプリの設定」画面の「フォーム」タブで次のテーブルを参考にフィールドの設定を確認・変更します。
フィールドの種類 |
フィールド名 |
フィールドコード |
文字列(1行) |
会社名 |
Company_name |
文字列(1行) |
部署名 |
Department |
文字列(1行) |
担当者名 |
Representative |
文字列(1行) |
郵便番号(数字のみ) |
Zip_code |
文字列(1行) |
TEL(数字のみ) |
Phone |
文字列(1行) |
FAX(数字のみ) |
Fax |
文字列(1行) |
住所 |
Address |
ドロップダウン |
顧客ランク |
Rank |
文字列(1行) |
メールアドレス |
Mail |
文字列(複数行) |
備考 |
Note |
レコード番号 |
レコード番号 |
record_no |
数値 |
緯度 |
lat |
数値 |
経度 |
lng |
次に「設定」タブを開き、「JavaScript / CSSでカスタマイズ」をクリックして、以下のJavaScriptファイル、CSSファイルのURLを指定し、設定を保存します。
JavaScriptファイル
- https://js.cybozu.com/vuejs/v3.4.20/vue.global.prod.js
- https://cdn.jsdelivr.net/npm/vuetify@3.5.6/dist/vuetify.min.js
CSSファイル
- https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/css/materialdesignicons.min.css
- https://cdn.jsdelivr.net/npm/vuetify@3.5.6/dist/vuetify.min.css
STEP2:カスタムビューでのVue.jsのテンプレート開発
固定リンクがコピーされました
今度は、「一覧」タブで、「+」サインをクリックして、一覧を追加します。
一覧設定画面で、以下を設定します。
- 一覧名
- 表示形式:「カスタマイズ」を選択
- HTML:後述のVueテンプレート
- ページネーションを表示する:未選択
また、「一覧ID」をメモしておいてください。
以下のようにVuetify.jsでテンプレートを作成します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
<!--
* vue.js + vuetify.js + Custom view sample css
* Copyright (c) 2019 Cybozu
*
* Licensed under the MIT License
* https://opensource.org/license/mit/
-->
<div id="app">
<v-app>
<v-main>
<v-container fluid>
<template v-if="!detailView">
<v-card>
<v-card-title>
顧客一覧
<v-spacer></v-spacer>
<v-text-field
v-model="search"
prepend-inner-icon="mdi-magnify"
label="検索"
single-line
hide-details
></v-text-field>
</v-card-title>
<v-data-table
:headers="headers"
:items="customers"
:search="search"
class="elevation-1"
>
<template v-slot:item="props">
<tr>
<td>
<v-icon
large
color="primary"
@click="showDetail(props.item)"
icon="mdi-magnify"
>
</v-icon>
</td>
<td>{{ props.item.record_no.value }}</td>
<td>{{ props.item.Company_name.value }}</td>
<td>{{ props.item.Department.value }}</td>
<td>{{ props.item.Representative.value }}</td>
<td>{{ props.item.Address.value }}</td>
</tr>
</template>
</v-data-table>
</v-card>
</template>
<template v-else>
<v-card>
<v-card-actions>
<v-btn size="large" variant="outlined" color="primary" @click="back">一覧に戻る</v-btn>
<v-btn size="large" variant="flat" color="primary" @click="save">変更を保存</v-btn>
</v-card-actions>
<v-divider></v-divider>
<v-card-title>顧客詳細</v-card-title>
<v-card-text>
<v-row justify="space-around">
<v-col cols="12" sm="6" md="4">
<v-text-field
v-model="customer.Company_name.value"
label="会社名"
variant="outlined"
>
</v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field
v-model="customer.Department.value"
label="部署名"
variant="outlined"
>
</v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field
v-model="customer.Representative.value"
label="担当者名"
variant="outlined"
>
</v-text-field>
</v-col>
</v-row>
<v-row justify="space-around">
<v-col cols="12" sm="6" md="4">
<v-text-field
v-model="customer.Zip_code.value"
label="郵便番号"
variant="outlined"
>
</v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field
v-model="customer.Phone.value"
label="電話番号"
variant="outlined"
>
</v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field
v-model="customer.Fax.value"
label="Fax"
variant="outlined"
>
</v-text-field>
</v-col>
</v-row>
<v-row justify="space-around">
<v-col cols="12" sm="6" md="6">
<v-text-field
v-model="customer.Address.value"
label="住所"
variant="outlined"
>
</v-text-field>
</v-col>
<v-col cols="12" sm="6" md="6">
<v-select
v-model="customer.Rank.value"
:items="rankList"
label="顧客ランク"
variant="outlined"
>
</v-select>
</v-col>
</v-row>
<v-row justify="space-around">
<v-col cols="12">
<v-text-field
v-model="customer.Mail.value"
label="メールアドレス"
variant="outlined"
>
</v-text-field>
</v-col>
</v-row>
<v-row justify="space-around">
<v-col cols="12">
<v-text-field
v-model="customer.Note.value"
label="備考"
variant="outlined"
>
</v-text-field>
</v-col>
</v-row>
<v-row justify="space-around">
<v-col cols="12" sm="6">
<v-text-field
v-model="customer.record_no.value"
label="レコード番号"
disabled
variant="outlined"
>
</v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-text-field
v-model="customer.lat.value"
label="緯度"
variant="outlined"
>
</v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-text-field
v-model="customer.lng.value"
label="経度"
variant="outlined"
>
</v-text-field>
</v-col>
</v-row>
</v-card-text>
</v-card>
</template>
</v-container>
</v-main>
</v-app>
</div>
|
以上の設定後、変更を保存します。
また、以下のようにcssをカスタマイズすることで、一覧の行表示の色分けが可能です。
「vuetify_sample.css」のように適当なファイル名をつけて保存し、「JavaScript / CSSでカスタマイズ」の設定画面で、アップロードします。
1
2
3
4
5
6
7
8
9
10
|
/*
* vue.js + vuetify.js + Custom view sample css
* Copyright (c) 2019 Cybozu
*
* Licensed under the MIT License
* https://opensource.org/license/mit/
*/
tbody tr:nth-of-type(odd) {
background-color: rgba(0, 0, 0, .05);
}
|
一覧表示と詳細表示をdetailView
のフラグで切り替えています。
1
2
3
4
5
6
|
<template v-if="!detailView">
...
</template>
<template v-else>
...
</template>
|
ページのタイトルと検索フィールドを表示します。
search
プロパティーでテーブルのフィルター機能と連携しています。
v-model
で指定することにより、双方向のデータバインディングを実現しています。
1
2
3
4
5
6
7
8
9
10
11
|
<v-card-title>
顧客一覧
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="検索"
single-line
hide-details
></v-text-field>
</v-card-title>
|
一覧テーブルを表示します。
フッター部分にページ切り替え表示を加えます。
headers
items
search
のデータをバインディングしています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<v-data-table
:headers="headers"
:items="customers"
:search="search"
class="elevation-1"
>
<template v-slot:item="props">
<tr>
<td>
<v-icon
large
color="primary"
@click="showDetail(props.item)"
icon="mdi-magnify"
>
</v-icon>
</td>
<td>{{ props.item.record_no.value }}</td>
<td>{{ props.item.Company_name.value }}</td>
<td>{{ props.item.Department.value }}</td>
<td>{{ props.item.Representative.value }}</td>
<td>{{ props.item.Address.value }}</td>
</tr>
</template>
</v-data-table>
|
詳細表示のトップにボタンを配置します。
今回は、「一覧に戻る」と「変更を保存」のボタンを作成しました。
1
2
3
4
|
<v-card-actions>
<v-btn size="large" variant="outlined" color="primary" @click="back">一覧に戻る</v-btn>
<v-btn size="large" variant="flat" color="primary" @click="save">変更を保存</v-btn>
</v-card-actions>
|
顧客の詳細情報を表示します。
ここで変更したフィールドのデータは即座に一覧のデータにも反映されていますが、「変更を保存」しない限り、kintoneのデータベースへは変更が反映されていません。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
<v-card-text>
<v-row justify="space-around">
<v-col cols="12" sm="6" md="4">
<v-text-field
v-model="customer.Company_name.value"
label="会社名"
variant="outlined"
>
</v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field
v-model="customer.Department.value"
label="部署名"
variant="outlined"
>
</v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field
v-model="customer.Representative.value"
label="担当者名"
variant="outlined"
>
</v-text-field>
</v-col>
</v-row>
.
.
.
<v-card-text>
|
STEP3:Vue.jsによるプログラムの開発
固定リンクがコピーされました
以下のサンプルコードを参考にVue.jsのプログラムを作成します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
/*
* vue.js + vuetify.js + Custom view sample program
* Copyright (c) 2019 Cybozu
*
* Licensed under the MIT License
* https://opensource.org/license/mit/
*/
(()=> {
kintone.events.on('app.record.index.show', (event) => {
if (event.viewId !== 123) { // 作成したカスタマイズビューのIDを指定
return event;
}
// Vue3とVuetify3を使用する準備
const {createApp, ref, reactive, toRefs, computed} = Vue;
const vuetify = Vuetify.createVuetify();
const appId = kintone.app.getId();
const query = kintone.app.getQuery();
kintone.api(kintone.api.url('/k/v1/records', true), 'GET', {app: appId, query: query}, (resp) => {
const app = createApp({
setup() {
const detailView = ref(false);
const customers = ref(resp.records);
const customer = reactive({});
const search = ref('');
const rankList = ['A', 'B', 'C'];
const headers = [
{title: '詳細表示', key: 'actions', sortable: false},
{title: 'レコード番号', key: 'record_no.value'},
{title: '会社名', key: 'Company_name.value'},
{title: '部署名', key: 'Department.value'},
{title: '担当者名', key: 'Representative.value'},
{title: '住所', key: 'Address.value'}
];
// 詳細表示のアイコンクリック
const showDetail = (item) => {
detailView.value = true;
Object.assign(customer, item);
};
// 一覧に戻るボタンクリック
const back = () => {
detailView.value = false;
};
// 変更を保存ボタンクリック
const save = () => {
const param = {
app: appId,
id: customer.record_no.value,
record: {
Company_name: {value: customer.Company_name.value},
Department: {value: customer.Department.value},
Representative: {value: customer.Representative.value},
Zip_code: {value: customer.Zip_code.value},
Phone: {value: customer.Phone.value},
Fax: {value: customer.Fax.value},
Address: {value: customer.Address.value},
Rank: {value: customer.Rank.value},
Mail: {value: customer.Mail.value},
Note: {value: customer.Note.value},
lat: {value: customer.lat.value},
lng: {value: customer.lng.value}
}
};
kintone.api(kintone.api.url('/k/v1/record', true), 'PUT', param)
.then(() => alert('データが更新されました。'))
.catch(err => alert(err.message));
};
return {...toRefs({detailView, customers, customer, search, rankList, headers}), showDetail, back, save};
}
});
app.use(vuetify).mount('#app');
});
return event;
});
})();
|
プログラム作成後、「sample_vue.js」等のファイル名を指定して、保存し、kintoneの「JavaScript / CSSでカスタマイズ」の設定画面にて、アップロードします。
上記でメモしておいた一覧IDが一致した場合のみ、一覧表示のイベントで処理を続行します。
また、「ページネーションを表示する」を外したため、イベントの発生時にレコードが取得されていません。
よって、kintone APIより、レコードの一括取得を行います。
最大取得数は500件ですが、何も指定しない場合、初期値は100件までです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
kintone.events.on('app.record.index.show', (event) => {
if (event.viewId !== 123) { // 作成したカスタマイズビューのIDを指定
return event;
}
// VueとVuetifyを使用する準備
const {createApp, ref, reactive, toRefs, computed} = Vue;
const vuetify = Vuetify.createVuetify();
const appId = kintone.app.getId();
const query = kintone.app.getQuery();
kintone.api(kintone.api.url('/k/v1/records', true), 'GET', {app: appId, query: query}, (resp) => {
// ...
});
return event;
});
|
createApp()
でVue.jsのインスタンスを生成し、テンプレートのDOMにマウントします。
また、このときVuetifyを利用するためにuse()
でVuetifyを読み込みます。
1
2
3
4
|
const app = createApp({
// ...
});
app.use(vuetify).mount('#app');
|
setup
フックでは、以下の変数を定義しています。
detailsView
:画面切り替えフラグ
customers
:kintoneから取得した顧客レコード一覧情報
customer
:一覧から選択した顧客の詳細情報、
search
:検索フィールドで入力した文字列
rankList
:顧客ランクのドロップダウンの値
headers
:一覧のヘッダーのデータオブジェクト
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
setup() {
const detailView = ref(false);
const customers = ref(resp.records);
const customer = reactive({});
const search = ref('');
const rankList = ['A', 'B', 'C'];
const headers = [
{title: '詳細表示', key: 'actions', sortable: false},
{title: 'レコード番号', key: 'record_no.value'},
{title: '会社名', key: 'Company_name.value'},
{title: '部署名', key: 'Department.value'},
{title: '担当者名', key: 'Representative.value'},
{title: '住所', key: 'Address.value'}
];
|
以下は、ボタンがクリックされたときに実行される関数を定義しています。
showDetail
メソッド:顧客一覧で詳細表示のアイコンがクリックされたとき
back
メソッド:「一覧に戻る」ボタンがクリックされたとき
save
メソッド:「変更を保存」ボタンがクリックされたとき
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
// 詳細表示のアイコンクリック
const showDetail = (item) => {
detailView.value = true;
Object.assign(customer, item);
};
// 一覧に戻るボタンクリック
const back = () => {
detailView.value = false;
};
// 変更を保存ボタンクリック
const save = () => {
const param = {
app: appId,
id: customer.record_no.value,
record: {
Company_name: {value: customer.Company_name.value},
Department: {value: customer.Department.value},
Representative: {value: customer.Representative.value},
Zip_code: {value: customer.Zip_code.value},
Phone: {value: customer.Phone.value},
Fax: {value: customer.Fax.value},
Address: {value: customer.Address.value},
Rank: {value: customer.Rank.value},
Mail: {value: customer.Mail.value},
Note: {value: customer.Note.value},
lat: {value: customer.lat.value},
lng: {value: customer.lng.value}
}
};
kintone.api(kintone.api.url('/k/v1/record', true), 'PUT', param)
.then(() => alert('データが更新されました。'))
.catch(err => alert(err.message));
};
|
STEP4:動作確認
固定リンクがコピーされました
一覧表示選択ドロップダウンより、作成したカスタマイズビューを選択します。
「検索」フィールドに検索したい文字列を入力すると、一覧の絞り込みが即座に行われます。
次に詳細表示のアイコンをクリックします。
詳細画面が表示されるので、「会社名」を適当に変更し、「変更を保存」します。
保存が成功した後「一覧に戻る」をクリックします。
一覧で「会社名」の変更が即座に反映されています。
このサンプルでは、取得した100件までのレコードに対するレコードの検索絞り込みやページネーションのみ有効になります。
また、検索フィールドで絞り込みできるフィールドは、一覧に表示したフィールドのみです。
カスタマイズビューをVue.jsとVuetify.jsを使って作成すると一覧と詳細ページがシングルページで比較的簡単に作成できます。
一覧の検索も入力に応じて絞り込みできたり、デバイスの画面の大きさに対して表示を切り替えできたりする機能を実現できます。
Vue.jsは、シングルページアプリケーションや携帯端末のアプリケーションの開発に適していますので、ぜひ、試してみてはいかがでしょうか?