Webix を使ってみたの続き

# はじめに

HTML＋JavaScript でアプリ開発するにあたって、UI ライブラリ Webix を使ってみました。

[Webix を使ってみた #JavaScript - Qiita](https://qiita.com/tinymouse/items/7ba3a624d53283860e55)

Webix のコンポーネントは多くの種類が用意されていますが特に、データを一覧表示する `datatable` コンポーネントは便利です。

# Webix の datatable を使ってみる

`datatable` は、テーブルにデータを表示するコンポーネントで、スクロールおよびソートを始め、データを表示したり編集するための機能が充実しています。

[DataTable - DataTable UI widget documentation: configuration, data export, etc. - Webix Docs](https://docs.webix.com/datatable__index.html)

## Webix の datatable を使ってみる①

### datatable コンポーネントを記述する

まず、`datatable` コンポーネントを画面に表示します。

```js
weibx.ready(function(){
    webix.ui({
        rows:[
            {   
                view:'datatable',
                id:"list",
                columns:[
                    { id:'rank', header:"", width:30 },
                    { id:'title', header:"Title", width:300 },
                    { id:'year', header:"Released", width:80 },
                    { id:'votes', header:"Votes", width:100	}
                ],
            },
        ]
    });
});
```

この `datatable` コンポーネントにデータをセットして表示します。

![サンプル①.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114460/141f2e8d-e181-af4c-08c1-2a22b46d97c5.png)

### datatable にデータをセットする①

まず、`data` プロパティを使って設計時点でデータを指定します。

```js
        {   
            view:'datatable',
            id:"list",
            (中略)
            data:[
                { id:1, title:"The Shawshank Redemption", year:1994, votes:678790, rating:9.2, :1 },
                { id:2, title:"The Godfather", year:1972, votes:511495, rating:9.2, rank:2 }
            ]
        },
```

参考 https://snippet.webix.com/04e56172

### datatable にデータをセットする②

`data` プロパティを使って設計時点でデータをセットする代わりに、`parse` メソッドを使って実行時点でデータをセットします。

```js
        { 
            view:'datatable', 
            id:"list",
            (中略)
        }
    (中略)
    
    $$("list").parse([
        { id:1, title:"The Shawshank Redemption", year:1994, votes:678790, rating:9.2, rank:1 },
        { id:2, title:"The Godfather", year:1972, votes:511495, rating:9.2, rank:2 }
    ]);
```

参考 https://snippet.webix.com/598f77de
    
### データを返すサーバアプリを用意する			

サーバからデータを取得して表示したいので、GET リクエストを受けて JSON データを返すウェブサーバを用意します。

以降のサンプルは、Webix 公式が用意してくれている URL を使っています。

### datatable にデータをセットする③

`data` プロパティに JavaScript の配列オブジェクトをセットする代わりに、`url` プロパティにウェブサーバを指定します。

```js
        {   
            view:'datatable',
            id:"list",
            (中略)
            url:"https://docs.webix.com/samples/15_datatable/01_loading/data/data.json"
        },
```

参考 https://snippet.webix.com/045e83f8

### datatable にデータをセットする④

`url` プロパティを使って設計時点でデータをセットする代わりに、`load` メソッドを使って実行時点でデータをセットします。

```js
        { 
            view:'datatable', 
            id:"list",
            (中略)
        }
    (中略)

    $$('list').load("https://docs.webix.com/samples/15_datatable/01_loading/data/data.json");
```

参考 https://docs.webix.com/api__link__ui.datatable_load.html

## Webix の datatable を使ってみる②				
				
### datatable を編集可能にする

`editable` プロパティを指定すると、セル単位でインライン編集できるようになります。

```js
        {   
            view:'datatable',
            id:"list",
            (中略)
            columns:[
                { id:'rank', header:"", width:30 },
                { id:'title', header:"Title", width:300, editor:'text' },
                { id:'year', header:"Released", width:80, editor:'text' },
                { id:'votes', header:"Votes", width:100, editor:'text' }
            ],
            editable:true,
        },
```

参考 https://snippet.webix.com/f1edcadc

さらに、データの追加と削除できるように入力フォームを用意しましょう。

![サンプル②.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114460/4b71f44b-2239-6e14-0e50-4d87a67241d9.png)

### datatable にデータを追加する

`add` メソッドを使って `datatable` に行を追加できます。

まず、入力フォームを追加します。ここに「追加」ボタンを用意しています。

```js
        { 
            view:'datatable', 
            id:"list",
            (中略)
        },
        { 
            view:'form',
            id:"input",
            elements:[
                { view:'text', name:"title", value:"New Movie",inputWidth:200 },
                { view:'text', name:"year", value:"2025", inputWidth:200 },
                { cols:[
                    { view:'button', width:100, value:"Add", click:addData }, 
                ] }
            ]
        }
```

「追加」ボタンをクリックすると関数 `addData` を実行するようにしています。ここで `datatable` に行を追加します。

```js
    function addData(){
        var values = $$('input').getValues();
        $$('list').add({
            title: values['title'],
            year: values['year']
        });
    }
```

参考 https://snippet.webix.com/247a9541

### datatable にデータを削除する			

`remove` メソッドを使って `datatable` から行を削除できます。

まず、入力フォームに「削除」ボタンを追加します。

```js
        { 
            view:'datatable', 
            id:"list",
            (中略)
            select:'row',
        },
        { 
            view:'form',
            (中略)
                    { view:"button", width:160, value:"Remove selected", click:removeData }, 
                ] }
            ]
        }
```

「削除」ボタンをクリックすると関数 `removeData` を実行するようにしています。ここで `datatable` から行を削除します。

```js
    function removeData(){
        var id = $$('list').getSelectedId()
        if (!id) {
            return;
        }
        $$('list').remove(id);
    }
```

選択した行を削除するので、`datatable` に `select:'row'` を指定して行単位で選択できるようにしておきます。↓

```js
        {   
            view:'datatable',
            id:"list",
            (中略)
            select:`row`,
        },
```

参考 https://snippet.webix.com/247a9541

## Webix の datatable を使ってみる③				
				
### データを受けるサーバアプリを用意する

サーバにデータを送って記録したいので、POST リクエストを受けてデータを記録するウェブサーバを用意します。

以降のサンプルは、ウェブアプリのクライアントプログラムをテストできるサイトを利用しています。[httpbin.org](https://httpbin.org/#/HTTP_Methods/delete_delete)

### datatable で編集したデータを記録する①

`datatable` の `save` プロパティにウェブサーバを指定します。

```js
        {   
            view:'datatable',
            id:"list",
            (中略)
            url:"https://docs.webix.com/samples/15_datatable/01_loading/data/data.json"

            save:"https://https://httpbin.org/post",
        },
```

参考 https://docs.webix.com/api__link__ui.datatable_save_config.html

Webix の `datatable` コンポーネントから以下の POST リクエストが送られるようです。

```
追加時
{
    queryString=, postData=FileUpload, contentLength=50.0, contextPath= 
    parameters={
      webix_operation=[Ljava.lang.Object;@7b411371, year=[Ljava.lang.Object;@405f2985, title=[Ljava.lang.Object;@37c83540
    }, 
    parameter={
        webix_operation=insert, 
        title=New Movie, year=2025
    }, 
}

更新時
{
    queryString=, postData=FileUpload, contentLength=80.0, contextPath=, 
    parameters={
        webix_operation=[Ljava.lang.Object;@494b62ca, id=[Ljava.lang.Object;@79b1cbee, title=[Ljava.lang.Object;@510feef6, year=[Ljava.lang.Object;@3b7609aa, votes=[Ljava.lang.Object;@3e522055, 
    }, 
    parameter={
        webix_operation=update, 
        id=1736151956759, title=New Movie, year=2025, votes=999999,
    }
}

削除時
{
    queryString=, postData=FileUpload, contentLength=80.0, contextPath=, 
    parameters={
        webix_operation=[Ljava.lang.Object;@6628475f, id=[Ljava.lang.Object;@55a35e5b, title=[Ljava.lang.Object;@25cae84d, year=[Ljava.lang.Object;@77f08f85 
    }, 
    parameter={
        webix_operation=delete, 
        id=1735646393786, title=New Movie, year=2025, votes=999999
    }
}
```

`webix_operation` 属性がセットされているので、これで判別して処理できそうです。

### datatable で編集したデータを記録する②

`save` プロパティでウェブサーバを指定する代わりに、データが変更されたイベントを受けて `webix.ajax().post` メソッドを使ってウェブサーバにデータを送ることにします。

`datatable` に行が追加されるタイミングで `onBeforeAdd` イベント、表示の内容が変更されると `onDataUpdate` イベント、行が削除されるタイミングで `onBeforeDelete` イベントが発生します。

```js
    $$('list').attachEvent('onBeforeAdd', function(id, data, index){
        webix.ajax().post("https://httpbin.org/post", data)
    });
    
    $$('list').attachEvent('onDataUpdate', function(id, data, old){
        webix.ajax().post("https://httpbin.org/post", data)
    });
    
    $$('list').attachEvent('onBeforeDelete', function(id){
        var data = $$('list').getItem(id);
        webix.ajax().post("https://httpbin.org/post", data)
    });
```

`webix.ajax()` コンポーネントから以下の POST リクエストが送られます。

```
追加時
{
    queryString=, postData=FileUpload, contentLength=44.0, contextPath=, 
    parameters={
        id=[Ljava.lang.Object;@1259bd3, title=[Ljava.lang.Object;@3dfdc651, year=[Ljava.lang.Object;@64f7624b
    }, 
    parameter={
        id=1736152703287, title=New Movie, year=2025
    }
}

更新時
{
    queryString=, postData=FileUpload, contentLength=57.0, contextPath=, 
    parameters={
        id=[Ljava.lang.Object;@7684661a, title=[Ljava.lang.Object;@a7dd8bb, year=[Ljava.lang.Object;@3ebad4fb, votes=[Ljava.lang.Object;@111c2aee
    }, 
    parameter={
        id=1736152703287, title=New Movie, year=2025, votes=999999
    }
}

削除時
{
    queryString=, postData=FileUpload, contentLength=57.0, contextPath=,
    parameters={
        id=[Ljava.lang.Object;@5b8a2409, title=[Ljava.lang.Object;@334f35ba, year=[Ljava.lang.Object;@41831fb3, votes=[Ljava.lang.Object;@1a4be87a, 
    }, 
    parameter={
        id=1736152703287, title=New Movie, year=2025, votes=999999
    } 
}
```

`post()` する時点で、送信する `data` オブジェクトに追加か更新か削除か判別するための属性を追加する必要あります。あるいは、操作ごとに送信先の URL を別々にしてもいいでしょう。

## Webix の datatable を使ってみる④				

### datatable に新規追加の空行を追加する

一覧表示の最終に空行を表示してデータを入力して追加できると便利ですね。

![サンプル③.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114460/7fac9957-20a6-acc9-8526-f310d7210b42.png)

`onBeforeRender` イベントで空行を追加します。

```js
        {   
            view:'datatable',
            id:"list",
            columns:[
                { id:'no', header:"", width:30 },
                { id:'title', header:"Title", width:300, editor:'text' },
                { id:'year', header:"Released", width:80, editor:'text' },
                { id:'votes', header:"Votes", width:100, editor:'text' }
            ],
            editable:true,
        },
    (中略)
    
    $$('list').attachEvent('onBeforeRender', function(){
        var dt = this;
        if (dt.count() == 0){
            dt.add({});
        }
        if (canAdd(dt.getLastId())) {
            dt.add({});
        }
        dt.data.each(function(obj, i){
            obj['no'] = i + 1;
        });
    });
```

先頭の列を「no」にして、連番を表示するようにしています。↑

### 行を追加していいか判定する

上記のコードで `canAdd` 関数を用意して使っています。指定された行の、全てのセルが空でないか確認して、空がなければ行の追加を可能と判断します。↓

```js
    function canAdd(id) {
        return $$('list').getColumns(true).every(function(obj){
            return ($$('list').getItem(id)[obj.id])
        });
    }
```

### datatable で編集したデータを記録する③

`onBeforeAdd` イベントが発生する空行を追加した時点では全ての項目は空なので、ここで記録する処理しても意味ありません。
`onDataUpdate` イベントでも全ての項目がセットされていないと、処理してはだめでしょう。`canAdd` を使って判定します。

```js
    $$('list').attachEvent('onBeforeAdd', function(id, data){
        // 何もしない
    });
    
    $$('list').attachEvent('onDataUpdate', function(id, data){
        if (!canAdd(id)) {
            return;
        }
        webix.ajax().post("https://httpbin.org/post", data);
    });
```

`webix.ajax()` コンポーネントから以下の POST リクエストが送られます。`post()` する時点で追加か更新か判別して処理できていません。

```
追加・更新時
{
    queryString=, postData=FileUpload, contentLength=62.0, contextPath=,
    parameters={
        id=[Ljava.lang.Object;@725ff6bf, title=[Ljava.lang.Object;@e3169fd, year=[Ljava.lang.Object;@cf80f2, votes=[Ljava.lang.Object;@6dfc719f, no=[Ljava.lang.Object;@b5e3d70, 
    }, 
    parameter={
        id=1736153323221, title=New Movie, year=2025, votes=999999, no=3, 
    }, 
}
```

`id` を使ってサーバ側で登録済か確認して、未登録なら追加、そうでなければ更新の処理するのがいいでしょう。