スペースフィールドの活用

にメンテナンス済み

kintone のフォーム設定で配置できる「スペース」フィールドは、JavaScript カスタマイズでカスタム要素を挿入するための領域として活用できます。ボタンやステータス表示、独自の入力フォーム、集計結果の表示など、標準フィールドでは実現できない UI を画面内に組み込むことが可能です。

この記事では、スペースフィールドの基本的な使い方から実践的な活用パターンまで解説します。

スペースフィールドとは

スペースフィールドは、kintone のフォーム設定で配置できる要素の一つです。データを保持するフィールドではなく、フォーム上のレイアウト目的の空白領域ですが、JavaScript からアクセスするための 要素 ID を設定できます。

要素 ID の設定方法

  1. アプリの設定画面 → フォームタブを開く
  2. 「スペース」をフォーム上の任意の位置にドラッグ&ドロップ
  3. 配置したスペースをクリックし、歯車アイコンから設定を開く
  4. 「要素 ID」に任意の ID(英数字)を入力
  5. フォームを保存し、運用環境に反映する
チェック

要素 ID には英数字とアンダースコアが使用できます。フィールドコードと混同しないように、スペース要素であることがわかる命名(例: space_actions, sp_chart)にすると管理しやすくなります。

基本的な使い方

kintone.app.record.getSpaceElement

スペースフィールドの DOM 要素を取得するには kintone.app.record.getSpaceElement を使用します。

basic-space.js
(() => {
  'use strict';

  const events = ['app.record.detail.show', 'app.record.edit.show'];

  kintone.events.on(events, (event) => {
    // スペースフィールドの要素を取得(要素IDを指定)
    const spaceElement = kintone.app.record.getSpaceElement('my_space');

    if (!spaceElement) {
      console.error('スペース要素が見つかりません');
      return event;
    }

    // カスタム要素を追加
    const message = document.createElement('p');
    message.textContent = 'ここにカスタムコンテンツを表示できます';
    message.style.color = '#333';
    spaceElement.append(message);

    return event;
  });
})();
チェック

getSpaceElement はレコード詳細画面、作成画面、編集画面で使用できます。レコード一覧画面では使用できません。一覧画面にカスタム要素を追加するには kintone.app.getHeaderSpaceElement() を使用してください。

モバイル版での使用

モバイル版では別のメソッドを使用します。

mobile-space.js
// PC版
const spaceElement = kintone.app.record.getSpaceElement('my_space');

// モバイル版
const mobileSpaceElement = kintone.mobile.app.record.getSpaceElement('my_space');

実践パターン

パターン 1: アクションボタンの配置

レコード詳細画面にアクションボタンを配置する例です。

action-buttons.js
(() => {
  'use strict';

  kintone.events.on('app.record.detail.show', (event) => {
    const spaceElement = kintone.app.record.getSpaceElement('space_actions');
    if (!spaceElement) return event;

    // ボタンのコンテナ
    const container = document.createElement('div');
    container.style.display = 'flex';
    container.style.gap = '8px';
    container.style.padding = '8px 0';

    // PDF出力ボタン
    const pdfButton = document.createElement('button');
    pdfButton.textContent = '📄 PDF出力';
    pdfButton.style.cssText = 'padding: 8px 16px; border: 1px solid #ccc; border-radius: 4px; cursor: pointer; background: #fff;';
    pdfButton.onclick = () => {
      window.print();
    };

    // コピーボタン
    const copyButton = document.createElement('button');
    copyButton.textContent = '📋 URL をコピー';
    copyButton.style.cssText = 'padding: 8px 16px; border: 1px solid #ccc; border-radius: 4px; cursor: pointer; background: #fff;';
    copyButton.onclick = () => {
      navigator.clipboard.writeText(location.href);
      copyButton.textContent = '✅ コピーしました';
      setTimeout(() => {
        copyButton.textContent = '📋 URL をコピー';
      }, 2000);
    };

    container.append(pdfButton, copyButton);
    spaceElement.append(container);

    return event;
  });
})();

パターン 2: ステータスバッジの表示

フィールドの値に応じたビジュアルなステータス表示を追加する例です。

status-badge.js
(() => {
  'use strict';

  /** ステータスごとの設定 */
  const STATUS_CONFIG = {
    未対応: { color: '#e74c3c', icon: '🔴' },
    対応中: { color: '#f39c12', icon: '🟡' },
    完了: { color: '#27ae60', icon: '🟢' },
    保留: { color: '#95a5a6', icon: '⚪' },
  };

  const events = ['app.record.detail.show', 'app.record.create.show', 'app.record.edit.show'];

  kintone.events.on(events, (event) => {
    const spaceElement = kintone.app.record.getSpaceElement('space_status');
    if (!spaceElement) return event;

    const status = event.record['対応状況'].value;
    const config = STATUS_CONFIG[status] || { color: '#333', icon: '⚫' };

    const badge = document.createElement('div');
    badge.style.cssText = `
      display: inline-flex;
      align-items: center;
      gap: 8px;
      padding: 8px 16px;
      border-radius: 20px;
      background-color: ${config.color}15;
      border: 2px solid ${config.color};
      font-size: 14px;
      font-weight: bold;
      color: ${config.color};
    `;
    badge.textContent = `${config.icon} ${status}`;

    spaceElement.append(badge);

    return event;
  });
})();

パターン 3: 関連レコードの集計表示

スペースフィールドに他のアプリのレコードを集計して表示する例です。

summary-display.js
(() => {
  'use strict';

  kintone.events.on('app.record.detail.show', async (event) => {
    const spaceElement = kintone.app.record.getSpaceElement('space_summary');
    if (!spaceElement) return event;

    const customerCode = event.record['顧客コード'].value;

    try {
      // 関連する注文レコードを取得(別アプリ)
      const response = await kintone.api(
        kintone.api.url('/k/v1/records.json', true),
        'GET',
        {
          app: 20, // 注文管理アプリのID
          query: `顧客コード = "${customerCode}" order by 注文日 desc limit 5`,
          fields: ['注文番号', '注文日', '金額', 'ステータス'],
        }
      );

      // 集計テーブルを作成
      const table = document.createElement('table');
      table.style.cssText = 'width: 100%; border-collapse: collapse; font-size: 13px;';

      // ヘッダー行
      const thead = document.createElement('thead');
      thead.innerHTML = `
        <tr style="background: #f5f5f5;">
          <th style="padding: 8px; border: 1px solid #ddd; text-align: left;">注文番号</th>
          <th style="padding: 8px; border: 1px solid #ddd; text-align: left;">注文日</th>
          <th style="padding: 8px; border: 1px solid #ddd; text-align: right;">金額</th>
          <th style="padding: 8px; border: 1px solid #ddd; text-align: left;">ステータス</th>
        </tr>
      `;
      table.append(thead);

      // データ行
      const tbody = document.createElement('tbody');
      let totalAmount = 0;

      response.records.forEach((record) => {
        const amount = Number(record['金額'].value) || 0;
        totalAmount += amount;

        const row = document.createElement('tr');
        row.innerHTML = `
          <td style="padding: 8px; border: 1px solid #ddd;">${record['注文番号'].value}</td>
          <td style="padding: 8px; border: 1px solid #ddd;">${record['注文日'].value}</td>
          <td style="padding: 8px; border: 1px solid #ddd; text-align: right;">¥${amount.toLocaleString()}</td>
          <td style="padding: 8px; border: 1px solid #ddd;">${record['ステータス'].value}</td>
        `;
        tbody.append(row);
      });

      table.append(tbody);

      // タイトルと合計を表示
      const title = document.createElement('h4');
      title.textContent = `最近の注文履歴(合計: ¥${totalAmount.toLocaleString()})`;
      title.style.cssText = 'margin: 0 0 8px 0; color: #333;';

      spaceElement.append(title, table);
    } catch (error) {
      console.error('注文履歴の取得に失敗しました:', error);
      spaceElement.textContent = '注文履歴の取得に失敗しました';
    }

    return event;
  });
})();

パターン 4: カスタム入力フォーム

標準では用意されていない入力 UI をスペースフィールドに設置する例です。

custom-input.js
(() => {
  'use strict';

  kintone.events.on(['app.record.create.show', 'app.record.edit.show'], (event) => {
    const spaceElement = kintone.app.record.getSpaceElement('space_color_picker');
    if (!spaceElement) return event;

    const label = document.createElement('label');
    label.textContent = 'テーマカラー: ';
    label.style.cssText = 'font-size: 14px; font-weight: bold;';

    const colorInput = document.createElement('input');
    colorInput.type = 'color';
    colorInput.value = event.record['テーマカラー'].value || '#3498db';
    colorInput.style.cssText = 'width: 50px; height: 30px; border: none; cursor: pointer;';

    // カラーピッカーの値変更時にフィールドに反映
    colorInput.oninput = () => {
      const { record } = kintone.app.record.get();
      record['テーマカラー'].value = colorInput.value;
      kintone.app.record.set({ record });
    };

    spaceElement.append(label, colorInput);

    return event;
  });
})();

重複挿入の防止

画面表示イベントが複数回発火した場合に要素が重複して追加されないよう、既存の要素を確認してから追加する工夫が重要です。

prevent-duplicate.js
(() => {
  'use strict';

  const ELEMENT_ID = 'custom-action-buttons';

  kintone.events.on('app.record.detail.show', (event) => {
    const spaceElement = kintone.app.record.getSpaceElement('space_actions');
    if (!spaceElement) return event;

    // 既に要素が追加されていたら何もしない
    if (document.getElementById(ELEMENT_ID)) {
      return event;
    }

    const container = document.createElement('div');
    container.id = ELEMENT_ID;

    // ボタンなどの要素を追加
    const button = document.createElement('button');
    button.textContent = 'アクション';
    container.append(button);

    spaceElement.append(container);

    return event;
  });
})();
チェック

一覧画面でも同様に、ビュー切り替え時にイベントが再発火して要素が重複する場合があります。一意の ID を付与して存在チェックを行うか、spaceElement.innerHTML = '' で一度クリアしてから追加する方法が有効です。

レコード一覧・詳細画面にボタンを設置するサンプル
kintoneカスタマイズを行う際によく使われる、レコード一覧・詳細画面にボタンを設置する方法を紹介します。

まとめ

  • スペースフィールドに要素 ID を設定し、getSpaceElement で DOM 要素を取得できる
  • ボタン、ステータス表示、集計テーブル、カスタム入力など多彩な要素を配置できる
  • レコード詳細・作成・編集画面で使用可能(一覧画面では別の API を使う)
  • モバイル版では kintone.mobile.app.record.getSpaceElement を使用する
  • 要素の重複挿入を防ぐため、ID チェックやクリア処理を実装する
#kintone #JavaScript #TypeScript