GAS + Vue.js + Vuetify でスプレッドシートの編集履歴を表示する
main.gs
// ページを表示する
function doGet() {
const htmlOutput = HtmlService.createTemplateFromFile("index").evaluate();
htmlOutput
.setTitle('スプレッドシート編集履歴')
.addMetaTag('viewport', 'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui');
return htmlOutput;
}
// クライアントサイドから呼ぶ
function getLogSheetData() {
const logSheet = SpreadsheetApp.getActive().getSheetByName('Log');
const logSheetRows = logSheet.getRange(1, 1, logSheet.getLastRow(), logSheet.getLastColumn()).getDisplayValues();
const columnHeadings = logSheetRows.shift();
const editLogRows = logSheetRows;
return {
columnHeadings: columnHeadings,
editLogRows: editLogRows
};
}
// スプレッドシートの編集をトリガーに実行する
function insertLog(e) {
if (e.source.getSheetName() === 'Log') return;
const logSheet = SpreadsheetApp.getActive().getSheetByName('Log');
logSheet.appendRow([
e.source.getSheetName(),
e.user.getEmail(),
Moment.moment().format('YYYY-MM-DD HH:mm:ss'),
]);
}
// 各ファイルの読み込みに使う
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
js.html
<script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>
<script>
Vue.component('edit-log-list', {
template: '\
<v-simple-table fixed-header>\
<template v-slot:default>\
<thead>\
<tr>\
<th class="text-center" v-for="columnHeading in columnHeadings">{{ columnHeading }}</th>\
</tr>\
</thead>\
<tbody>\
<tr v-for="editLogRow in editLogRows">\
<td class="text-right" v-for="editLogCell in editLogRow">{{ editLogCell }}</td>\
</tr>\
</tbody>\
</template>\
</v-simple-table>\
',
props: ['columnHeadings', 'editLogRows']
});
Vue.component('emit-button', {
template: '\
<v-btn large @click="onClick">{{ label }}</v-btn>\
',
props: ['label'],
methods: {
onClick: function() {
this.$emit('clicked');
}
}
});
var vm = new Vue({
el: '#app',
vuetify: new Vuetify(),
data: {
columnHeadings: [],
editLogRows: [[]]
},
methods: {
updateEditLogs: function() {
google.script.run
.withSuccessHandler(function(logSheetData) {
vm.editLogRows = logSheetData.editLogRows;
})
.withFailureHandler(function(arg) {
alert("Logシートからデータを取得できませんでした");
}).getLogSheetData();
}
},
created: function() {
google.script.run
.withSuccessHandler(function(logSheetData) {
vm.columnHeadings = logSheetData.columnHeadings;
vm.editLogRows = logSheetData.editLogRows;
})
.withFailureHandler(function(arg) {
alert("Logシートからデータを取得できませんでした");
}).getLogSheetData();
}
});
</script>
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= include('css'); ?>
</head>
<body>
<div id="app">
<v-app>
<v-content>
<v-container>
<div class="d-flex justify-center mb-6">
<emit-button @clicked="updateEditLogs" label="更新"></emit-button>
</div>
<div class="d-flex justify-center">
<edit-log-list :column-headings="columnHeadings" :edit-log-rows="editLogRows"></edit-log-list>
</div>
</v-container>
</v-content>
</v-app>
</div>
<?!= include('js'); ?>
</body>
</html>
css.html
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
<style>
</style>
これらのファイルをスプレッドシートと結びついたスクリプトエディタで作成し、[公開] > [ウェブアプリケーションとして導入] > [導入] という操作を経ることで、簡易的な Web アプリが完成します。
ユーザーがスプレッドシートを編集すると Log シートに「シート名」「編集者」「日時」が書き込まれます。アプリ側では、ページの読み込み時と、[更新] ボタンの押下時に Log シートの内容が取得されます。
ハマったところ
Class google.script.run でサーバーサイド (main.gs) で定義した関数をクライアントサイド (js.html) で呼び出して、その返り値をコールバック関数に渡すことができます。ただ、公式ドキュメントによれば、渡せる値には制限があるようです。今回の場合、
google.script.run
.withSuccessHandler(function(logSheetData) {
vm.columnHeadings = logSheetData.columnHeadings;
vm.editLogRows = logSheetData.editLogRows;
})
.withFailureHandler(function(arg) {
alert("Logシートからデータを取得できませんでした");
}).getLogSheetData();
getLogSheetData() の返り値をコールバック関数が受け取るのですが、その返り値に Date オブジェクトが含まれていたせいで、Vue インスタンスの data プロパティの各値が null になっていました(つまりコールバック関数の引数が null になっていました)。そこで対策として getLogSheetData() 内の
const logSheetRows = logSheet.getRange(
1, 1,
logSheet.getLastRow(), logSheet.getLastColumn()
).getValues();
という記述を以下に変更しました。
const logSheetRows = logSheet.getRange(
1, 1,
logSheet.getLastRow(), logSheet.getLastColumn()
).getDisplayValues();
違いは getValues() が getDisplayValues() になっただけです。getValues() だとスプレッドシート上の日時表記が Date オブジェクトとして取得されてしまうので、getDisplayValues() ですべてを文字列として取得するようにします。
所感
GAS のスクリプトエディタで Vue.js を書くのは、このくらいの規模が(精神的に)限界かもしれません。
この記事が気に入ったらサポートをしてみませんか?