0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

kViewer標準検索では足りない絞り込みをJavaScriptで補う

0
Posted at

はじめに

単純に「レコードを検索したい」だけであれば、まずは標準機能で実現できるかを確認するのがよいと思います。

一方で、業務で使う検索画面として考えると、標準検索フォームだけでは使いにくい場面があります。

たとえば、次のようなケースです。

  • 複数の条件を1つの画面でまとめて指定したい
  • 選択肢を自由入力ではなく、実データから自動生成したい
  • 選択肢を業務上の見やすい順番で並べたい
  • 親項目を選んだあとに、子項目の候補を絞り込みたい
  • 数値や日時を範囲指定したい
  • 選択した条件をURLに反映して、kViewer一覧を絞り込みたい

この記事では、kViewer標準検索を置き換えるのではなく、標準検索では柔軟に作りにくい業務向けの絞り込みUIをJavaScriptで補う考え方をまとめます。

なお、kViewerのJavaScript・CSSカスタマイズは、プレミアムコース以上で利用できます。また、JavaScriptによるカスタマイズは動作保証およびサポートの対象外となるため、実際の環境で十分に動作確認したうえで利用してください。リストビューのバージョンによって、JavaScriptカスタマイズのイベントや記述方法が異なる場合があります。実装時は、使用しているビューのバージョンに対応した公式ガイドを確認してください。

この記事で扱うこと・扱わないこと

扱うこと

この記事では、以下を扱います。

  • kViewer標準検索でできること
  • kViewer標準検索だけでは作りにくいこと
  • JavaScriptで絞り込みUIを補う理由
  • 外部公開APIから選択肢を取得する考え方
  • 自由入力ではなく選択肢にするメリット
  • 選択肢をJavaScript側で業務順に並べる考え方
  • 親子関係のある条件を段階的に絞り込む考え方
  • 数値や日時を範囲条件として扱う考え方
  • additionalFilters を生成してkViewer一覧へ反映する考え方
  • URLが長くなりすぎる可能性への注意

扱わないこと

この記事では、以下は扱いません。

  • kViewerの基本的な作成手順
  • kViewer標準検索フォームの詳細な設定手順
  • FormBridge連携の詳細
  • 認証やアクセス制限の設計
  • レコードを1件選択してフォームへ値を反映する実装

この記事のテーマは、あくまで kViewer標準検索では柔軟に作りにくい絞り込みUIを、JavaScriptとadditionalFiltersでどう補うか です。

想定読者

この記事は、以下のような方を想定しています。

  • kViewerの標準検索だけでは業務に合わないと感じている方
  • kViewer一覧に、独自の絞り込みボタンやモーダルを追加したい方
  • 外部公開APIから取得した値を検索条件の選択肢にしたい方
  • 自由入力ではなく、選択式で絞り込み条件を指定させたい方
  • additionalFilters を使ってkViewer一覧の絞り込みを制御したい方

kViewer標準検索でできること

kViewerには、ビュー上でレコードを検索するための標準機能があります。

公式の操作ガイドでは、kViewerのレコード検索機能として以下の4種類が紹介されています。

検索機能 概要
kintone風フォーム kintoneのレコード一覧画面での絞り込みのように、閲覧者がフィールド・式・値を指定する検索方法
固定フォーム 管理画面であらかじめ設定したフィールドを使い、決められた条件で検索する方法
リンクリスト カテゴリーごとのリンクを作成し、クリックで該当カテゴリーの一覧を表示する方法
まとめて検索 フリーワードで検索する方法

標準検索でも、用途によっては十分便利です。

たとえば、以下のような用途であれば標準機能で対応しやすいです。

  • 公開ビュー上でキーワード検索したい
  • あらかじめ決めた項目で検索させたい
  • カテゴリーごとのリンクを用意したい
  • 簡単なフリーワード検索を用意したい

また、まとめて検索では、スペース区切りで複数ワードのAND検索ができます。

なお、標準検索機能の利用可否や配置場所は、契約コースやビューの種類によって異なります。詳細は公式の操作ガイドを確認してください。

標準検索だけでは作りにくいこと

標準検索でできることを踏まえたうえで、今回JavaScriptで補った理由を整理します。

ポイントは、標準では絞り込みができない ではなく、標準では業務に合わせた絞り込みUIを柔軟に作りにくい ということです。

複数条件を1つの業務UIにまとめたい

標準検索では、検索フォームの種類ごとに決められた形で検索します。

しかし業務画面としては、以下のような絞り込み条件を1つのモーダルにまとめたいことがあります。

  • 条件
  • 区分
  • 分類
  • 日時
  • ステータス

単に検索フォームを置くだけではなく、業務の流れに合わせて、条件をグルーピングしたり、入力方法を変えたりしたい場合があります。

また、標準検索フォームを複数設置した場合、それぞれのフォームは 単独で動作 します。そのため、異なる種類の条件を1回の操作でまとめて指定したい場合は、独自の検索UIを用意する方法が考えられます。

選択肢を外部データから動的に作りたい

自由入力の検索では、利用者が正しいキーワードを知っている必要があります。

たとえば、条件やステータスのような項目では、利用者が文字を直接入力するよりも、存在する値から選べる方が使いやすいです。

外部公開APIから候補値を取得して選択肢にすると、次のメリットがあります。

  • 入力ミスを減らせる
  • 表記ゆれを減らせる
  • 存在する値だけを選ばせられる
  • 選択肢の追加・変更に追従しやすい
  • 利用者が検索キーワードを覚えていなくても使える

たとえば、外部公開APIから取得したレコードをもとに、条件の選択肢を作るようなイメージです。

JavaScript
const getUniqueValues = (records, fieldCode) => {
  const values = [];

  records.forEach((record) => {
    const field = record[fieldCode];
    if (!field) {
      return;
    }

    const textValue = String(field.value);
    if (textValue && !values.includes(textValue)) {
      values.push(textValue);
    }
  });

  return values;
};

このように、APIから取得した値を一意化して、検索条件の選択肢として使えます。

選択肢を業務順に並べたい

標準検索の使い方によっては、選択肢の並び順を業務上の見やすい順番にしたいことがあります。

たとえば、ステータスであれば、単純な文字順ではなく、業務フローの順番で並べたい場合があります。

選択肢
購買入力中
購買確認中
購買承認待ち
経理財務確認中
経理財務承認待ち
完了
否認

このような場合、レコードの並び順を変えるのではなく、検索条件として表示する選択肢をJavaScript側で業務順に並べる という考え方にします。

JavaScript
const sortStatusValues = (values) => {
  const getRank = (value) => {
    if (value.includes('購買') && value.includes('入力中')) return 10;
    if (value.includes('購買') && value.includes('確認中')) return 20;
    if (value.includes('購買') && value.includes('承認待ち')) return 30;
    if (value.includes('経理財務') && value.includes('確認中')) return 40;
    if (value.includes('経理財務') && value.includes('承認待ち')) return 50;
    if (value === '完了') return 80;
    if (value === '否認') return 90;
    return 999;
  };

  return values.slice().sort((a, b) => getRank(a) - getRank(b));
};

この方法なら、一覧レコードのソートとは別に、検索UIの選択肢だけを見やすくできます。

親子関係のある条件を段階的に絞り込みたい

種類や区分のようなデータでは、親子関係を持つ選択肢があります。

たとえば、以下のような関係です。
大分類を親項目、中分類と小分類を子項目とする例で説明します。

段階的な絞り込み
大分類
  ↓
中分類
  ↓
小分類

この場合、最初からすべての中分類や小分類を表示すると、候補が多くなりすぎます。

そこで、大分類を選んだら、それに紐づく中分類だけを表示し、中分類を選んだら、それに紐づく小項目だけを表示するようにします。

JavaScript
majorSelect.onchange = () => {
  middleSelect.innerHTML = '';
  minorSelect.innerHTML = '';

  const items = [...new Set(
    records
      .filter((record) => record['大分類'].value === majorSelect.value)
      .map((record) => record['中分類'].value)
  )].sort();

  items.forEach((item) => {
    const option = document.createElement('option');
    option.value = item;
    option.textContent = item;
    middleSelect.appendChild(option);
  });
};

このような段階的な選択UIは、標準検索フォームだけでは作りにくいため、JavaScriptで補う価値があります。

数値や日時を範囲指定したい

数値や日時の範囲条件そのものではなく、利用者が独自モーダル上で開始値と終了値を入力し、その内容を一覧へ反映するUIを作りたい場合は、JavaScriptでadditionalFiltersを生成する方法が考えられます。

たとえば、以下のような条件です。

  • 時間が30分以上
  • 時間が120分以下
  • 日時が指定期間内

このような条件は、検索用の入力欄を用意し、入力された値から additionalFilters を生成すると扱いやすくなります。

JavaScript
if (timeMin) {
  filters.push({
    field: '時間',
    sign: '>=',
    value: String(timeMin),
    with: 'and'
  });
}

if (timeMax) {
  filters.push({
    field: '時間',
    sign: '<=',
    value: String(timeMax),
    with: 'and'
  });
}

JavaScriptで補う方針

今回の方針は、kViewer標準検索を否定するものではありません。

標準機能でできることは標準機能に任せ、標準検索だけでは作りにくい部分をJavaScriptで補います。

やりたいこと 標準機能 JavaScriptで補うこと
kViewer上で検索する できる 補わない
検索フォームを配置する できる 補わない
複数条件を業務画面としてまとめる 作りにくい場合がある モーダルUIを作る
選択肢を外部データから作る 柔軟な動的生成は難しい 外部公開APIから取得する
選択肢を業務順に並べる 標準設定だけでは任意の業務順に制御しにくい場合がある JavaScriptで並び替える
親子関係のある条件を段階的に絞る 業務要件に応じた連動UIは作りにくい onchangeで候補を切り替える
数値・日時を範囲指定する 要件による 条件オブジェクトを生成する
絞り込み条件をURLへ反映する 標準内 additionalFiltersを生成する

全体構成

全体構成は以下です。

処理の流れは以下です。

実装の考え方

1. 一覧画面に絞込検索ボタンを追加する

kViewerの一覧画面表示イベントで、画面上に絞込検索ボタンを追加します。
設置箇所はヘッダーコンテンツ内に入れることをおすすめします。

JavaScript
kviewer.events.on('view.index.show', (context) => {
  if (!context.getViewElement()) {
    return;
  }

  const button = document.createElement('button');
  button.type = 'button';
  button.textContent = '絞込検索';

  button.addEventListener('click', () => {
    openModal();
  });

  context.getViewElement().prepend(button);
});

この時点では、ボタンを押したらモーダルを開くだけです。

2. 外部公開APIから選択肢を取得する

検索条件の選択肢を、外部公開APIから取得します。

以下は、1ページ分のレコードを取得する最小構成です。全件を取得する場合は、外部公開APIのページング仕様に従って複数ページを取得する必要があります。

JavaScript
const fetchAllRecords = async (firstPageUrl) => {
  const response = await fetch(firstPageUrl);
  const data = await response.json();

  return Array.isArray(data.records) ? data.records : [];
};

実際にはページングがあるため、ページ番号を切り替えながら、必要なページのレコードを取得します。

ページ番号を切り替えながら必要なレコードを取得し、各ページのレスポンスを同じ形式に整えて結合します。

JavaScript
const createPageUrl = (url, page) => {
  return url.replace(/\/\d+$/, `/${page}`);
};

こちらの例は、外部公開APIのURLがページ番号で終わる形式を前提としています。実際のURL形式に合わせて調整してください。

3. 選択肢を一意化する

取得したレコードから、検索条件として使う値を取り出します。

JavaScript
const getUniqueValues = (records, fieldCode) => {
  const values = [];

  records.forEach((record) => {
    const field = record[fieldCode];
    if (!field) {
      return;
    }

    const textValue = String(field.value);
    if (textValue && !values.includes(textValue)) {
      values.push(textValue);
    }
  });

  return values;
};

これにより、条件やステータスの値を検索用の選択肢として利用できます。

4. 必要に応じて業務順に並び替える

選択肢は、単純な文字順ではなく、業務上の見やすい順番に並べたいことがあります。

JavaScript
const sortProcessTypeValues = (values) => {
  const order = ['新規', '変更', '廃番'];

  const orderedValues = [];
  const extraValues = [];

  order.forEach((definedValue) => {
    if (values.includes(definedValue)) {
      orderedValues.push(definedValue);
    }
  });

  values.forEach((value) => {
    if (!order.includes(value)) {
      extraValues.push(value);
    }
  });

  return orderedValues.concat(extraValues);
};

このようにすると、定義済みの値は業務順に並べ、それ以外の値は末尾に残せます。

5. モーダル内に条件入力UIを作る

モーダル内には、条件に応じて入力部品を配置します。

たとえば、以下のように使い分けます。

条件 UIの例
条件 セレクトボックス
区分 チェックボックス
大分類 セレクトボックス
中分類 セレクトボックス(大分類に応じて変わる)
小分類 セレクトボックス(中分類に応じて変わる)
日時 開始・終了の日時入力欄
ステータス チェックボックス

モーダル自体は、検索条件を入力するためのUIです。
kViewerのレコードを直接編集するための画面ではありません。

6. 選択された条件からadditionalFiltersを生成する

選択された条件を、additionalFilters に変換します。

JavaScript
const filters = [];

if (conditionsValue) {
  filters.push({
    field: '条件',
    sign: 'in',
    value: [conditionsValue],
    with: 'and'
  });
}

if (selectedStatusValues.length > 0) {
  filters.push({
    field: 'ステータス',
    sign: 'in',
    value: selectedStatusValues,
    with: 'and'
  });
}

複数条件をANDでつなぎたい場合は、with: 'and' を指定する形にします。

7. URLに条件を付与して遷移する

最後に、生成した additionalFilters をURLに付与して、絞り込み済みのkViewer一覧へ遷移します。

JavaScript
const moveToFilteredUrl = (filters) => {
  const nextUrl = new URL(location.href);

  nextUrl.searchParams.delete('additionalFilters');

  if (filters.length > 0) {
    nextUrl.searchParams.set('additionalFilters', JSON.stringify(filters));
  }

  location.href = nextUrl.toString();
};

これにより、モーダルで選択した条件をkViewer一覧に反映できます。

URLが長くなりすぎる可能性

additionalFilters はURLのクエリパラメータとして渡します。
そのため、条件を増やしすぎるとURLが長くなります。

kViewer固有の明確な文字数上限は確認できていませんが、URL長にはブラウザやサーバー側の実装上の制約があります。

そのため、以下のような設計には注意が必要です。

  • チェックボックスの選択肢が非常に多い
  • 選択された値を大量にURLへ含める
  • 長い文字列の条件を複数含める
  • 複数の範囲条件や選択条件を詰め込みすぎる

実務上は、よく使う条件に絞る方が安全です。選択できる項目数に上限を設ける方法もあります。

大量の条件を扱う必要がある場合は、条件をすべてURLパラメーターに含める設計が適切か、改めて検討した方がよいです。

なお、URLに反映した検索条件は、ブラウザのアドレス欄や履歴に残ります。機密情報や個人情報を検索条件の値として含めないようにしてください。

このカスタマイズが向いているケース

この方法は、以下のようなケースに向いています。

  • 標準検索では検索条件のUIが業務に合わない
  • 利用者に自由入力させたくない
  • 外部公開APIから取得した値を選択肢にしたい
  • 支払条件やステータスを業務順に並べたい
  • 親項目を選ぶと子項目の候補が変わるUIを作りたい
  • 数値や日時を範囲指定したい
  • 絞り込み条件をURLに反映して、kViewer一覧を表示したい

逆に、単純なキーワード検索で十分な場合は、標準検索を使う方がよいです。

まとめ

kViewer標準検索でも検索や絞り込みはできます。
そのため、「標準では絞り込みができない」という理解は正しくありません。

正確には、標準では業務に合わせた絞り込みUIを柔軟に作りにくい場面がある ということです。

今回のポイントは、以下です。

  • 標準検索でできることは標準機能に任せる
  • 標準検索だけでは作りにくい業務向けUIをJavaScriptで補う
  • 自由入力ではなく、外部公開APIから取得した選択肢を使う
  • 選択肢はJavaScript側で業務順に並べる
  • 親子関係のある条件は段階的に絞り込む
  • 数値や日時は範囲条件として扱う
  • 最終的に additionalFilters を生成してkViewer一覧へ反映する
  • URLが長くなりすぎないように条件数には注意する

この考え方にすると、kViewerを業務に合わせた検索画面として使いやすくできます。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?