もっともハマった箇所の紹介。

  • ドロップダウンの選択肢にapiから取得した配列を入れる。
  • 初期データの一部のみを編集不可にする。

【要約・学んだこと】

ドロップダウンの選択肢にapiから取得した配列を入れる。

columnsで下記のようにdropdownの選択肢を設定している。

// Handsontable.vuedata: function() {
return {
test: "testttt",
hotSettings: {
... columns: [
{ data: "checkbox", type: "checkbox" },
{ data: "id", type: "text", placeholder: "ID", readOnly: true },
{ data: "name", type: "text", placeholder: "name" },
{ data: "mail", type: "text", placeholder: "E-mail" },
{
data: "department",
type: "dropdown",
placeholder: "department",
source: ["test", "test2", "test3"]
},
{ data: "position", type: "text", placeholder: "position" }
],
....
}
};
},

しかし、sourceから他のデータは参照できない。columnsを関数で記述する方法でも参照できないので、updateSettingsを利用する。難しい部分であるが大丈夫。

// Handsontable.vuedata: function() {
return {
hotSettings: {
... columns: [ ... {
data: "department",
type: "dropdown",
placeholder: "department"
// source: ["test", "test2", "test3"] ←これを削除
},

{ data: "position", type: "text", placeholder: "position" }
],
... }
};
},
created: async function() {
await this.$emit("getTableContents");
await this.setCellMeta();
},
methods: {
... setCellMeta: function() {
const departmentData = this.department;
this.$refs.hotTable.hotInstance.updateSettings({
cells: function(row, col, prop) {
const cellProperties = {};
// ドロップダウンメニュー項目の設定
if (prop === "department") {
cellProperties.source = departmentData;
}
return cellProperties;
}
});
},

まずはhotSettingsのcolumnsで設定したsourceを削除。

次にsetCellMeta関数を定義。非常に重要なテクニックとして、setCellMetaの最初に宣言した

const departmentData = this.department;

がある。

$refs内部ではhotInstance以外のthisが参照できないため、this.departmentを一度ここで別の変数に割り当ててあげる必要がある。(重要テク)

また、ここではprop === departmentと一致するならば、という条件にしているが、

cells: function(row, col, prop) {
const cellProperties = {};
// ドロップダウンメニュー項目の設定
if (prop === "department") {
cellProperties.source = departmentData;
}
return cellProperties;
}

col === 4としても同じ結果となる。(propの方がどこに代入しているかコードを見てわかりやすい、順番を変えても変更が不要なためpropを利用)

これで選択肢にapiから取得したdepartmentデータを代入できた。

初期データの一部のみを編集不可にする。

このような設定がしたくなる場合も多々あるが、ここも難しい。先ほどのupdateSettingsに設定を追加する。IEだとうまくいかないパターンなどあるので、かなり繊細な部分となる。

今回は初期データのidとnameのみ編集不可能にする。

初期データが持っているデータがあるかないかで判別するため、今回はinitialというデータを与える。apiから取得しているが、テーブルで表示、しないデータがあればこれは不要。

getTableContents: async function() {
const getData = await api.all([this.getMembers(), this.getDepartment()]);
const membersData = getData[0].data;
const departmentData = getData[1].data;
membersData.map(key => (key.initial = true));this.members = membersData;
this.department = departmentData;
},

続いて本題の”初期データのみ編集不可にする”だが、下記のようにする。

// Handsontable.vue
data: function() {
return {
test: "testttt",
hotSettings: {
... columns: [
{ data: "checkbox", type: "checkbox" },
{ data: "id", type: "text", placeholder: "ID" }, ←ここのreadOnlyを削除
{ data: "name", type: "text", placeholder: "name" },
{ data: "mail", type: "text", placeholder: "E-mail" },
{
data: "department",
type: "dropdown",
placeholder: "department"
},
...
}
};
},
watch: {
department: function() {
this.setCellMeta();
}
},

created: async function() {
await this.$emit("getTableContents");
},
methods: {
... setCellMeta: function() {
const departmentData = this.department;
this.$refs.hotTable.hotInstance.updateSettings({
cells: function(row, col, prop) {
const cellProperties = {};
// ドロップダウンメニュー項目の設定
if (prop === "department") {
cellProperties.source = departmentData;
}
const table = this.instance.getSourceData();
// readOnlyにするセルの設定。
if (table[row]) {
if (prop === "id" && table[row].initial) {
cellProperties.readOnly = true;
}
if (prop === "name" && table[row].initial) {
cellProperties.readOnly = true;
}
}
return cellProperties;
}

});
},
... }
};

解説すると、

  • if(table[row])の意味
const table = this.instance.getSourceData();if (table[row]) {
if (prop === "id" && table[row].initial) {
cellProperties.readOnly = true;
}
if (prop === "name" && table[row].initial) {
cellProperties.readOnly = true;
}
}

if (table[row])とすることで、複数行をペーストした際に発生するundefinedエラーを回避する。実際は初回読み込み時以降、設定を更新したいわけではないが、updateSettingsは何か動作をするたびに実行されてしまう。

一度実行されたら再実行しないようにする関数を書いたが、これがIEだと正常に動作しない。そのためこのような形が万能である。

このif文がないと複数行をペーストした際、下記のようにエラーが発生する。

  • watchについて
created: async function() {
await this.$emit("getTableContents");
await this.setCellMeta();
},

としても、getTableContentsはresolveを返さないので非同期処理が行われないので、departmentカラムの選択肢が空になってしまう。

resolveを返すように書き換えるのも大変なので、departmentが更新されたタイミングで実行されるように、createdではなくwatachで実行するとうまくいく。

このようなよくわからない難所を潜り抜けるのがhandsontableの醍醐味。

これで理想通り行の挿入、ペーストなどを行っても問題なく初期データのみが編集不可となった。

Web上でExcel風のテーブルを実装するHandsontableを使う-4 へ続く。

【わからなかったこと】

$emitで呼ぶと返り値も変わるのか。

【感想】

またしても業務時とは違う部分でつまずいて困惑。本当に難しいライブラリだけど、今までの経験ですんなり乗り切れたのはハンズオンテーブラーとしては嬉しい限り。

業務では今回紹介した部分でめちゃくちゃハマったので、これを参考にさっと切り抜けてください。

--

--

Tatsuya Asami
Tatsuya Asami

Written by Tatsuya Asami

Front end engineer. React, TypeScript, Three.js

No responses yet