kintoneアプリ一覧画面で絞り込み検索がやりにくいので簡単な検索窓を付けるサンプル(随時コーディング)

kintoneアプリの一覧画面で絞り込みをするには、漏斗のマークを押して絞り込みをします。
しかしこの絞り込み画面はユーザーに使ってもらいにくい。作業を見ていると使っているのを見たことがありません。
そこで、一覧画面に絞り込み窓を追加してユーザーに使ってもらい易くしたいと考えました。

# 実装方法案
以下のように出来ないかと考えてみました。

1. 一覧画面に検索窓を設置する
2. 検索窓に入力してリターンするとイベント発火
3. 検索キーワードでレコードを検索
4. 一覧に表示する

# 実現方法を探る
だいたいこういうのは他の識者がやっているのでサンプルを探します。
以下参考になりそうなコードサンプルへのリンクです。
※Googleに「kintone 一覧画面 検索窓」と入れて検索してみました。

- [【kintone】シンプルなレコード内検索小窓を作ってみよう](http://crud-lab.hatenablog.com/entry/2016/03/17/9000)
- [javascriptで、kintoneのヘッダ部分に検索窓を付けてみた。](https://gist.github.com/fddcddhdd/eee42d9d2f573ba1b17e)

なんか簡単にできそうですね！

# URL内のクエリを使ったレコード条件指定

* [URL内のクエリで、表示するレコードの条件を指定](https://developer.cybozu.io/hc/ja/articles/202388664)

`https://subdomain.cybozu.com/k/appid/?view=1234567`

`https://subdomain.cybozu.com/k/appId/?query={クエリをUTF-8でURLエンコードした文字列}`

Chrome DevTools の console で クエリをURLエンコードする。

```
var q1 = '会社名="林田商会"';
console.log(encodeURI(q1));
%E4%BC%9A%E7%A4%BE%E5%90%8D=%22%E6%9E%97%E7%94%B0%E5%95%86%E4%BC%9A%22

// 文字列の時はこれでも同じ結果
console.log(encodeURI('会社名 in ("林田商会")'));

```

kintoneにログインして、一覧を表示した状態で、ブラウザの?query= にURLエンコードした文字列を繋げる。

`https://subdomain.cybozu.com/k/appId/?query=%E4%BC%9A%E7%A4%BE%E5%90%8D=%22%E6%9E%97%E7%94%B0%E5%95%86%E4%BC%9A%22`

使用できるクエリ

* [レコードの一括取得（クエリで条件を指定）](https://developer.cybozu.io/hc/ja/articles/202331474#step2)



# 検索窓と検索ボタンを設置する
実現方法の 1. 一覧画面に検索窓を設置してみます。
上記[【kintone】シンプルなレコード内検索小窓を作ってみよう](http://crud-lab.hatenablog.com/entry/2016/03/17/9000)をそのままに、jQueryバージョンで検索フォームとボタンを設置してみます。

```javascript
        //検索キーワード
        var search_word = $("<input>");
        search_word.attr({type: 'text', id: 'search_word'});
        
        //検索ボタン
        var search_button = $("<input>");
        search_button.attr({type: 'submit', value: 'search'});
        search_button.click(function() {
            keyword_search();
        });
```

```javascript
        var label = $('<label>');
        label.append('レコード内検索');
        label.append(search_word);
        label.append(search_button);
        $(kintone.app.getHeaderMenuSpaceElement()).append(label);
```

# 検索キーワードでレコードを検索
実現方法 3. 検索キーワードでレコードを検索してみます。
いろいろやり方はありますが、[【kintone】シンプルなレコード内検索小窓を作ってみよう](http://crud-lab.hatenablog.com/entry/2016/03/17/9000)を参考にURLにクエリパラメータを渡すやり方でやってみます。

```javascript

        //キーワード検索の関数
        function keyword_search(){
            var keyword = search_word.val();
            var str_query = '?query='+ FIELD_CODE +' like "' + keyword;
            if(keyword == ""){
                str_query = "";
            }else if(keyword != ""){
                str_query = '?query='+ FIELD_CODE +' like "' + keyword + '"';
            }
            //検索結果のURLへ
            document.location = location.origin + location.pathname + encodeURI(str_query);
        }
```

# まとめ

最後にソースを全部載せておきます。

```javascript

jQuery.noConflict();
// 検索したいフィールドの設定値（検索対象のフィールドに合わせて変更する）
const FIELD_CODE = 'title'; // この場合 title というフィールドコードを検索対象としています。
// レコード一覧表示のイベントハンドラー
(function ($) {
    'use strict';
    kintone.events.on("app.record.index.show", function (event) {
        //検索キーワード
        let search_word = $("<input>");
        search_word.attr({type: 'text', id: 'search_word'});

        //検索ボタン
        let search_button = $("<input>");
        search_button.attr({type: 'submit', value: 'search'});
        search_button.click(function() {
            keyword_search();
        });

        //キーワード検索の関数
        function keyword_search(){
            let keyword = htmlEscape(search_word.val());
            const str_query = preareQueryString(keyword);
            if (!str_query) {
                document.location = location.origin + location.pathname;
            } else {
                document.location = location.origin + location.pathname + encodeURI(str_query);
            }
        }

        //重複を避けるため要素をあらかじめクリアしておく
        let node_space = kintone.app.getHeaderMenuSpaceElement();
        for (let i = node_space.childNodes.length - 1; i >= 0; i--) {
            node_space.removeChild(node_space.childNodes[i]);
        }
        let label = $('<label>');
        label.append('レコード内検索');
        label.append(search_word);
        label.append(search_button);
        $(kintone.app.getHeaderMenuSpaceElement()).append(label);
        return event;
    });

    // HTMLエスケープ処理    
    const htmlEscape = (str) => {
        if (!str) return;
        return str.replace(/[&<>"'`]/g, (match) => {
            const escape = {
                '&': '&amp;',
                '<': '&lt;',
                '>': '&gt;',
                '"': '&quot;',
                "'": '&#39;',
                '`': '&#x60;'
            };
            return escape[match];
        });
    };

    // クエリ文字列生成    
    const preareQueryString = (str) => {
        if (!str) return;
        if (str === "") {
            return "";
        } else {
            return '?query='+ FIELD_CODE +' like "' + str + '"';
        }
    }; 

})(jQuery);
```

# 参考リンク
この記事は下記の方が書かれた記事を参考に、またコードをそのまま利用させていただきました。
この場を借りて感謝致します。ありがとうございます。

- 基本的な考え方とコードは[【kintone】シンプルなレコード内検索小窓を作ってみよう](http://crud-lab.hatenablog.com/entry/2016/03/17/9000)をそのままにお借りしています。

- jQueryを使ったフォーム部品の生成は[jQueryでHTMLタグ要素をcreateElement使わずに生成する](http://blog.toshimaru.net/jqueryhidden-inputjquery/)を参考に、またそのまま利用しています。

- jQueryを使ったフォーム部品の生成では[jQueryで新しい要素を作成する方法](http://www.koikikukan.com/archives/2013/07/16-003333.php)も参考にしています。

- jQuery全般については [jQueryの基礎](http://www.gadgety.net/shin/web/tech/jquery.html)を参考にさせていただきました。

- JavaScriptのDOM操作については[各種ノードの作り方と表示のさせ方をまとめておきました。（DOMおれおれAdvent Calendar 2015 – 10日目）](https://ginpen.com/2015/12/11/dom-creation-and-insertion/)を参考にさせていただきました。

ありがとうございます。

# kintone UI Component 版

```javascript
jQuery.noConflict();
//検索したいフィールドの設定値
const FIELD_CODE = 'title';
//レコード一覧表示のイベントハンドラー
(($) => {
    'use strict';
    kintone.events.on("app.record.index.show", (event) => {
        let kui_label = new kintoneUIComponent.Label({text: 'レコード検索'});
        //検索キーワード
        let search_word = '';
        let kui_text = new kintoneUIComponent.Text({value: ''});
        kui_text.on('change', (e) => {
            console.log('on change');
            console.log('value: ' + e.target.value);
            search_word = e.target.value;
        });
        //検索ボタン
        let kui_button = new kintoneUIComponent.Button({ text: '検索' });
        kui_button.on('click', () => {
            keyword_search();
        });

        //キーワード検索の関数
        function keyword_search(){
            console.log("search_word => ", search_word);
            let keyword = htmlEscape(search_word);
            const str_query = preareQueryString(keyword);
            if (!str_query) {
                document.location = location.origin + location.pathname;
            } else {
                document.location = location.origin + location.pathname + str_query;
            }
        }

        //重複を避けるため要素をあらかじめクリアしておく
        let node_space = kintone.app.getHeaderMenuSpaceElement();
        for (let i = node_space.childNodes.length - 1; i >= 0; i--) {
            node_space.removeChild(node_space.childNodes[i]);
        }
        let label = $('<label>');
        label.append(kui_label.render().getElementsByTagName("span")[0]);
        label.append(kui_text.render().getElementsByTagName("input")[0]);
        label.append(kui_button.render().getElementsByTagName("button")[0]);
        $(kintone.app.getHeaderMenuSpaceElement()).append(label);
        return event;
    });

    // HTMLエスケープ処理    
    const htmlEscape = (str) => {
        if (!str) return;
        return str.replace(/[&<>"'`]/g, (match) => {
            const escape = {
                '&': '&amp;',
                '<': '&lt;',
                '>': '&gt;',
                '"': '&quot;',
                "'": '&#39;',
                '`': '&#x60;'
            };
            return escape[match];
        });
    };

    // クエリ文字列生成    
    const preareQueryString = (str) => {
        if (!str) return;
        if (str === "") {
            return "";
        } else {
            return '?query='+ FIELD_CODE +' like "' + encodeURIComponent(str) + '"';
        }
    }; 
})(jQuery);
```

フィールドの位置が微妙なので、適宜CSSで調整するとよろしいかと思います。