Handsontableを使ってkintoneをExcelライクに入力しよう その1

フォローする

(著者:kintone エバンジェリスト 村濱 一樹

はじめに

kintoneはブラウザーからレコードの一覧の表示や編集ができますが、Excelのような画面で閲覧・編集がしたいという要望を度々聞きます。
今回はCybozu CDNで公開されている、Handsontableを使ってExcelライクな入力を実現してみました。 やり方を以下にまとめますので、興味のある方は是非試してみてください。

Handsontableとは

ホームページ: http://handsontable.com

HandsontableはExcelのようなスプレッドシートライクな入力を可能にしてくれるJavaScriptライブラリです。
サンプルページで試すとわかりますが、Excelのようにデータ入力ができるだけでなく、セルの書式の指定やチャートが作れたりと、機能が多いのも魅力です。

サンプルアプリの準備

まずはアプリの準備をします。

フィールドの設定

サンプルアプリのフィールドは以下です。フィールド名とフィールドコードは同一にしました。

フィールド名(フィールドコード) フィールドタイプ
レコード番号 レコード番号
会社名 文字列(1行)
先方担当者名 文字列(1行)
見込み時期 日付
確度 ラジオボタン
製品名 ドロップダウン
単価 数値
小計 計算

一覧の設定

アプリの一覧は、カスタマイズビューを用います。
スプレッドシート表示のための要素をHTMLで記述します。

<div id="sheet"></div>

カスタマイズビューについての詳細は下記を参照ください。

JavaScript/CSSの設定

今回はCybozu CDNに登録されているものを利用します。Cybozu CDNにはJavaScriptだけでなくCSSファイルも提供されている上、キャッシュも有効になっているので高速化が図れるという利点があります。

今回はversion 0.20.0を利用します。アプリのJavaScript/CSS設定画面には、下記URLを指定します。

データの入力

確認のために、サンプルデータを先に数件入力しておきましょう。

JavaScript

実際にJavaScriptでコーディングしていきます。

Handsontableの使い方

下記のように指定するだけでスプレッドシートの表示が可能です。 Handsontableのdataオプションにkintoneのレコードデータを指定すればバインドできそうです。

スプレッドシートにレコードを一覧表示する

まずは単純にkintoneのレコードをスプレッドシートに表示してみます。
Handsontableのdataオプションには、kintoneのレコードデータを指定し、columnsには"フィールド名.value"と指定することで、kintoneのレコードデータをそのまま反映することができます。
必要に応じて、colHeadersオプションに配列を指定することで、ヘッダ行も指定できます。
※viewIdはカスタマイズビュー設定時のviewIdを入力してください。

これでkintoneに登録したデータをExcelのように表示できるようになるはずです。

スプレッドシートに表示したレコードを更新する

表示することはできたので、その表示されたデータを更新できるようにします。
データの更新時、afterChangeオプションに指定したメソッドが呼び出されるので、そのメソッドを利用してkintoneのアップデート処理を行います。
また、columnsオプションを指定するときは、編集されたくないデータはreadOnlyの指定をすることができます。

afterChangeメソッドの引数Changeは下記の配列が格納されます。上記の例では、"変更があったセルの行数"を参照しどのレコード番号のデータを変更するかを指定しています。

ちなみに、データのバインディングは自動で行われますので、スプレッドシート上で変更したデータは、kintoneのevent.records変数にすでに反映されています。便利ですね。

最後に

いかがでしたか。 今回はExcelライクな見た目で、レコードの表示と編集が行えるようにkintoneをカスタマイズしましたが、レコードの追加やプルダウンによる選択などはまた次回に掲載したいと思います。

Handsontableを使ってkintoneをExcelライクに入力しよう その2>>

デモ環境

https://dev-demo.cybozu.com/k/37/

※デモ環境についての説明はこちら

記事に関するフィードバック

直接的に記事と関連がないご質問はcybozu developer コミュニティをご活用ください。

Avatar
Tor Solenssen

質問させてください。
提示された方法でアプリに組み込んでみたところ、ほぼ想定通りに動いてくれたのですが、
プロセス管理を有効にすると編集したものに更新されなくなります。
(画面上、編集はいったんされますが、画面切り替え等、更新のタイミングで
編集前の内容に戻ってしまいます)
おそらくは非同期処理に絡むと思われるのですが(ちがっていたらすみません)、
一覧表示イベントだとpromise関数は現状使えないようなので
何か別の方法で解消できないかと考えています。
例えば「更新」ボタンを設置し、手動で上書きタスクを強制的に走らせられれば解決できそうなのですが
現状でプロセス管理と同居できる方法はありますでしょうか?

Avatar
村濱一樹

当方でも現象を再現できました。確認したところ、プロセスを有効にした際に、「ステータス」「作業者」のフィールドも有効になるのですが、それが更新できなくてエラーになってしまっているようです。

なので、フィールドコードを除外するためのメソッドに「ステータス」「作業者」を追加してあげてください。


// kintoneのレコード更新時は、レコード番号などアップデートできないフィールドがあるので、除外するためのメソッド
var setParams = function(record) {
        : 中略
        if (['レコード番号', '作成日時', '更新日時', '作成者', '更新者', 'ステータス', '作業者'].indexOf(prop) === -1) {
Avatar
Tor Solenssen

ご回答ありがとうございます。
さっそく修正内容を追記しましたところ、プロセス管理を有効の状態でも編集が可能になりました。
新年早々、課題が一つ解消できて一安心です。
複数レコードを一括できるとても便利な機能なので、標準での実装を期待してます!

Avatar
藤原
村濱様、はじめまして。 こちらの記事を参考にさせていただき、項目名とビューIDを変更して使用してみたのですが、フィールドの編集をしても更新がされませんでした。 ルックアップを使用しているとまた別の処理も必要になるなど、制限があるのでしょうか。
Avatar
村濱一樹

回答が遅れてすいません。

> ルックアップを使用しているとまた別の処理も必要になるなど、制限があるのでしょうか。

ルックアップなら問題なさそうです。ただ、ルックアップはAPIからデータをいれる場合、コピー元がユニークである必要があります。 その設定を見なおしていただいてもよろしいでしょうか。

 

参考:REST APIを使ってルックアップフィールドの一括更新を行う
https://www.joyzo.co.jp/blog/293

掲示されているサンプルはJavaですが、kintoneの注意については同じなので確認をしてみてください。

村濱一樹により編集されました
Avatar
藤原

村濱様

ご返答頂きありがとうございます。私も返信が遅れ、失礼いたしました。

> ルックアップなら問題なさそうです。ただ、ルックアップはAPIからデータをいれる場合、コピー元がユニークである必要があります。 その設定を見なおしていただいてもよろしいでしょうか。

ご指摘いただいた通り、コピー元がユニークでないルックアップがあるのが原因でした。
なので、API発行時に更新したいフィールドのみを指定したら、思い通りの更新をすることができました。

大変助かりました。ありがとうございます。

Avatar
Hiroshi Yoshioka

質問させてください。

「アプリコード(レコード番号に前に付与する文字列)」を設定していないアプリでは、
本ページのサンプルのセル編集が正しく動作したのですが、

「アプリコード(student)」を設定したアプリでセルの編集を行うと、

{"code":"CB_VA01","id":"1505999166-246192175","message":"入力内容が正しくありません。","errors":{"records[0].id":{"messages":["整数で指定してください。"]}}}
というエラーが発生します。

Request Payloadを見ると、
{app: 7, records: [{id: "student-5970",…}], __REQUEST_TOKEN__: "~~~~"} とあり、
idにアプリコードが付与されているため、「整数で指定してください」というエラーが出たのだと思うのですが、
どう回避すればよいでしょうか?

Avatar
村濱一樹

 Hiroshi Yoshioka さん

編集、ということですので下記の記事(その2)を参考にされているということですよね。
https://cybozudev.zendesk.com/hc/ja/articles/207331836

今回のサンプルではアプリコードに対応してないので、アプリコードに対応させるようにする必要があります。

  // 変更があった行から、レコード追加か変更かを判断し、クエリをつくる
  for(i = 0; i < changedRows.length; i++) {
    if (records[changedRows[i]]["レコード番号"].value === null) {
      insertRecords.push(
        setParams(records[changedRows[i]])
      );
    } else {
      updateRecords.push({
        id: records[changedRows[i]]["レコード番号"].value,
        record: setParams(records[changedRows[i]])
      });
    }
  }

上記は、レコード番号をそのまま使用してしまっているので、下記のように直すと動くかと思います。
(未検証ですが)

  // 変更があった行から、レコード追加か変更かを判断し、クエリをつくる
  for(i = 0; i < changedRows.length; i++) {
    if (records[changedRows[i]].$id.value === null) {  // ←$idを使うようにする
      insertRecords.push(
        setParams(records[changedRows[i]])
      );
    } else {
      updateRecords.push({
        id: records[changedRows[i]].$id.value,         // ←$id使うようにする
        record: setParams(records[changedRows[i]]) 
      });
    }
  }

※アプリコードとは別に$idというフィールドがあり、そこには整数のIDがはいっています。

 

回答になってますでしょうか。不明点があればまたご連絡ください。

Avatar
Hiroshi Yoshioka

村濱様

ご返信、ありがとうございます。

>編集、ということですので下記の記事(その2)を参考にされているということですよね。

記事(その1)の「スプレッドシートに表示したレコードを更新する」を参考にしたコードで、
試しておりました。

アドバイス通り、
["レコード番号"]部分を.$idに置き換えることで、
無事、アプリコード付きアプリでも編集(PUT成功)することができました。

迅速なご対応、ありがとうございました!

Avatar
村濱一樹

いえいえ、

https://github.com/mura-/kintone-spreadsheet

プラグイン化したのもあるので、必要に応じて試してみてください。
(少なくともコードは参考になるかもしれません)

Avatar
Hiroshi Yoshioka

村濱様

>https://github.com/mura-/kintone-spreadsheet
>
>プラグイン化したのもあるので、必要に応じて試してみてください。

ありがとうございます。
Handsontableを直接利用すると、カラムのtypeやカラム名などをjsファイルに入力するのが手間でしたが、
設定画面でフィールドを選ぶだけで適切なtypeを自動で指定してくれて、非常に便利です!

現在、アプリのレコード数が1000以上あるため、
絞り込みができないのは苦しいのですが、
これが解決できれば、すぐに業務で利用したいと思います。(^^

Avatar
菅原直樹

下記コードを入れないと、ページ送りでおかしくなります。

        // カスタマイズビュー設定時に登録したHTMLの要素を指定します。
        var container = document.getElementById('sheet');
container.innerHTML='';

サンプルのアプリでもページ送りが可能な場合はおかしくなっていました。

本文も修正していただいた方が、今後のためにも良いと思います。

菅原直樹により編集されました
Avatar
村濱一樹

ページ送りを考慮してませんでした。ご連絡ありがとうございます。

 

Avatar
工藤 淳

社内抵抗が強く、浸透するまでということで旧版で運用しております。本件を新版でテストすると反映されるのですが、旧版では動きません。新・旧で使用に違いはあるのでしょうか?

Avatar
村濱一樹

連絡おくれました。

特に問題なく動くはずなのですが、、具体的にどう「動かない」のか教えてもらってもいいですか?(表示されない、データの表示がおかしい、など)

また、何かしらエラーがでている可能性がありますので、是非下記の通り確認してみてください。

https://cybozudev.zendesk.com/hc/ja/articles/207613916-%E5%8B%95%E3%81%8B%E3%81%AA%E3%81%84-%E3%81%9D%E3%82%93%E3%81%AA%E6%99%82%E3%81%AF%E3%83%87%E3%83%90%E3%83%83%E3%82%B0%E3%82%92%E3%81%97%E3%81%A6%E3%81%BF%E3%82%88%E3%81%86-%E5%85%A5%E9%96%80%E7%B7%A8

Avatar
工藤 淳


村瀬様
お世話になります。

Tips第7回カスタマイズビューを利用してみようを参考に一覧をカスタマイズ設定しました。その後本件を参考にjsとCSSをコピーし相対するフィールド名のみ変更して作成してみました。

新版ではデータ7件のみですが、ご教示の通り一覧表示されております。旧版はデータが3000件あり、右上で1-100と表記されるので100件は並んでほしいのですが表にならない状況です。

エラー表示も出ているのですが、新・旧とも同じjsとCSSを貼り付けているため、;がありません、構文エラーです、の意味もわからないままです。

12/6大阪で行われた【kintone devCamp初級編】kintone をカスタマイズしてルーチンワークをお手軽に!にも参加させていただき何とか理解を深めようと勉強中です。

初心者のため投稿も十分に内容を伝えることがままならない状況ですがよろしくお願いいたします。

工藤

 

 

 

Avatar
村濱一樹

これだけではやはり原因を特定するのは難しいですが....、
1つ気づいたのが、旧デザインの場合cssのスタイルがなぜか当たっていない気がします。


CSSがなにかおかしいのかもしれませんね、もしかすると、そのアプリに別のCSS、あるいはkintone全体にCSSやJSが当たっていて競合している、ということは有りえませんかね?
(設定確認はこちら https://help.cybozu.com/ja/k/admin/javascript_fullcustomize.html )

 

それを確認してみて、問題なければ、そのエラーが表示されているという画像を高解像度で送ってもらってもいいですか?
上記貼り付けられている画像が、小さくてよく見えなくて...。

Avatar
工藤 淳

村濱様

お世話になります。さっそくご回答いただき、恐縮です。

実寸で添付いたしました。アドバイスいただけますか。

工藤

Avatar
村濱一樹

ちょっと試している最中なのですが、 IE8モードで動いてるのが原因の1つな気がします。

問題の切り分けのためにChromeで試してもらうことって可能ですか?

Avatar
工藤 淳

村濱様

再三で恐れ入ります。

エラー表示が違う形で表示されましたので添付いたしました。どうぞよろしくお願いいたします。

工藤

Avatar
村濱一樹

こちらこそ再三のお願いになってしまいますが。。

右画像のdownload.do~の部分、クリックできるハズですので(クリック後はエラー該当箇所が表示されるはず)、クリックしたあとの画像もいただけますでしょうか。

Avatar
工藤 淳

村濱様

お世話になります。

こちらでよろしいでしょうか。

工藤

Avatar
村濱一樹

まさにその赤線の部分、タイプミスをしていませんでしょうか?
掲載されているコードとくらべて確認してみてください。

Avatar
工藤 淳

【表示されるjs】
(function() {
    "use strict";
    kintone.events.on(['app.record.index.show'], function(event) {
        // ビューIDを指定する(指定したビューID以外は処理しない)
        if (event.viewId !== 5287827) return;
        var records = event.records;
        // カスタマイズビュー設定時に登録したHTMLの要素を指定します。
        var container = document.getElementById('sheet');
        // Handsontableをインスタンス化
        var hot = new Handsontable(container, {
            // kintoneのレコードデータを指定
            data: records,
            minSpareRows: 0,
            // カラムのヘッダーを指定
            colHeaders: ["レコード番号", "部屋コード", "所有者", "利用者名", "利用人数", "来館予定日", "退館予定日", "来館日", "退館日", "車両数"],
            contextMenu: false,
            // dataオプションのどの列を表示するか指定する。
            columns: [
                {data: "レコード番号.value"},
                {data: "部屋コード.value"},
                {data: "所有者.value"},
                {data: "利用者名.value"},
                {data: "利用人数.value"},
                {data: "来館予定日.value"},
                {data: "退館予定日.value"},
                {data: "来館日.value"},
                {data: "退館日.value"},
                {data: "車両数.value"}
            ],
        });
    });
})();
【表示されなかったjs】
(function() {
    "use strict";
    kintone.events.on(['app.record.index.show'], function(event) {
        // ビューIDを指定する(指定したビューID以外は処理しない)
        if (event.viewId !== 5287827) return;
        var records = event.records;
        // カスタマイズビュー設定時に登録したHTMLの要素を指定します。
        var container = document.getElementById('sheet');
        // Handsontableをインスタンス化
        var hot = new Handsontable(container, {
            // kintoneのレコードデータを指定
            data: records,
            minSpareRows: 0,
            // カラムのヘッダーを指定
            colHeaders: ["レコード番号", "部屋コード", "所有者", "利用者名", "利用人数", "来館予定日", "退館予定日", "来館日", "退館日", "車両数"],
            contextMenu: false,
            // dataオプションのどの列を表示するか指定する。
            columns: [
                {data: "レコード番号.value"},
                {data: "部屋コード.value"},
                {data: "所有者.value"},
                {data: "利用者名.value"},
                {data: "利用人数.value"},
                {data: "来館予定日.value"},
                {data: "退館予定日.value"},
                {data: "来館日.value"},
                {data: "退館日.value"},
                {data: "車両数.value"}
            ],
        });
    });
})();

 村濱様

お世話になります。

修正を行いましたが、結果は同じでした。ただし、クロームで行うと本件このサイト通りの表示ができました。

残念ながらグループ内ルールではクローム×、IE8○のためこのままでは社内システムとして使えないこともわかりました。

いろいろご指導いただいたことで知識が増えました。今回はありがとうございました。今後もアドバイスいただけますようお願いいたします。

工藤

 
 
 
    
 
 
 
 
 
 
 
 
 
 
 
 
 
  
               
 
 
 
       
  
 
 
 
 
   
   
   
 
 
 

 

 
      
       
 
      
 
 
 
  
 
 
 
 
 
 
     
     
 
 
 
 
 
    
 
 
 
   
   
   

 

Avatar
村濱一樹

> 残念ながらグループ内ルールではクローム×、IE8○のためこのままでは社内システムとして使えないこともわかりました。

 

そうでしたか。。実はkintone自体もIE9以上しかサポートしておりませんし、他のクラウドサービスもそういう傾向にありますので
ルールが更改されることを願っております。

ログインしてコメントを残してください。
Powered by Zendesk