GAS Webアプリでスプレッドシートの顧客マスタをプルダウン表示する

公開日: 

顧客マスタから有効な顧客を選び、GAS Webアプリのプルダウンに表示するイメージ

Google Apps Script(GAS)のWebアプリで、スプレッドシートの 顧客マスタ から有効な顧客を読み込み、HTMLフォームのプルダウンに表示します。表示順もマスタで管理し、選んだ顧客コードと顧客名を作業依頼へ登録します。

ここで扱うのは、スプレッドシートのセルに設定する入力規則のプルダウンではありません。Webアプリ画面のHTML <select> を、google.script.run で取得したデータから作る処理です。

シリーズ全体の構成と完成画面は第0回で紹介しています。

1. 今回作るもの

ファイル用途
Code.gsgetActiveCustomers() を追加する
index.html顧客プルダウンの初期表示を読み込み中にする
script.html顧客マスタを読み込み、プルダウンへ追加する

2. 完成イメージ

  • 顧客マスタ シートから顧客を取得できる
  • 有効区分 が 有効 の顧客だけを表示できる
  • 表示順 の昇順でプルダウンに表示できる
  • 顧客コードと顧客名を作業依頼シートへ保存できる
  • 有効な顧客がいない場合に画面へメッセージを表示できる
  • 読み込みに失敗した場合は、空データの場合と分けてエラーを表示できる
作業依頼受付フォームに顧客、件名、依頼内容、担当者を入力した状態

3. 第5回のフォームを引き継ぐ

第5回では固定の顧客選択肢を使ってフォームから作業依頼を登録しました。今回の starter.zip は、その時点のファイルです。saveRequest(data) による登録処理は引き継ぎ、顧客欄の選択肢を顧客マスタから読み込む処理を追加します。フォームを開き直すと、更新したマスタの内容が選択肢に反映されます。

4. 使用するファイル

今回使用するファイルは Code.gs、index.html、style.html、script.html の4つです。

5. 顧客マスタの構成

項目用途
顧客コード作業依頼シートへ保存するコード
顧客名プルダウンに表示し、作業依頼シートへ保存する名称
メールアドレス後続のメール送信で使う項目。今回は画面に表示しない
表示順プルダウンの並び順
有効区分有効 の顧客だけを表示する

メール送信は第12回で扱います。今回の getActiveCustomers() はメールアドレスも返しますが、画面では表示も利用もしません。実データを使う場合は、画面側で使わない情報をサーバーから返す必要があるかも確認してください。

顧客マスタシート。顧客コード、顧客名、メールアドレス、表示順、有効区分の列がある

顧客マスタを作業依頼シートと分ける理由や、シートの列構成については第2回の記事で説明しています。

6. 顧客マスタを読み込むコード

Code.gs

function getActiveCustomers() {
  const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = spreadsheet.getSheetByName('顧客マスタ');

  if (!sheet || sheet.getLastRow() < 2) {
    return [];
  }

  const values = sheet.getDataRange().getValues();
  const headers = values[0];
  const rows = values.slice(1);
  const columnMap = createHeaderMap_(headers);

  return rows
    .map((row) => ({
      customerCode: row[columnMap['顧客コード']],
      customerName: row[columnMap['顧客名']],
      email: row[columnMap['メールアドレス']],
      sortOrder: Number(row[columnMap['表示順']] || 0),
      activeFlag: row[columnMap['有効区分']],
    }))
    .filter((customer) => customer.customerCode && customer.customerName && customer.activeFlag === '有効')
    .sort((a, b) => a.sortOrder - b.sortOrder)
    .map((customer) => ({
      customerCode: customer.customerCode,
      customerName: customer.customerName,
      email: customer.email || '',
    }));
}

function createHeaderMap_(headers) {
  return headers.reduce((map, header, index) => {
    map[header] = index;
    return map;
  }, {});
}

index.html

<select id="customer" name="customer" required>
  <option value="">読み込み中です</option>
</select>

script.html

画面表示時に顧客マスタを読み込み、プルダウンへ追加します。script.html には、次の3か所を追加します。

1. 変数の宣言

既存の変数宣言に、顧客プルダウンの要素を追加します。

const customerSelect = document.getElementById('customer');

2. 画面表示時の呼び出し

変数宣言の直後に、読み込み処理を呼び出します。この1行がないと、プルダウンは「読み込み中です」のまま変わりません。

loadCustomers();

3. 関数の追加

function loadCustomers() {
  customerSelect.disabled = true;
  showMessage('顧客マスタを読み込んでいます。', 'info');

  google.script.run
    .withSuccessHandler(renderCustomerOptions)
    .withFailureHandler(handleCustomerLoadFailure)
    .getActiveCustomers();
}

function renderCustomerOptions(customers) {
  customerSelect.innerHTML = '';

  const defaultOption = document.createElement('option');
  defaultOption.value = '';
  defaultOption.textContent = '選択してください';
  customerSelect.appendChild(defaultOption);

  if (!customers || customers.length === 0) {
    customerSelect.disabled = true;
    showMessage('有効な顧客が登録されていません。顧客マスタを確認してください。', 'error');
    return;
  }

  customers.forEach((customer) => {
    const option = document.createElement('option');
    option.value = customer.customerCode;
    option.dataset.name = customer.customerName;
    option.textContent = customer.customerName;
    customerSelect.appendChild(option);
  });

  customerSelect.disabled = false;
  showMessage('入力後に登録ボタンを押してください。', 'info');
}

function handleCustomerLoadFailure(error) {
  customerSelect.innerHTML = '<option value="">読み込みに失敗しました</option>';
  customerSelect.disabled = true;
  showMessage(error && error.message ? error.message : '顧客マスタの読み込みに失敗しました。', 'error');
}

7. 顧客マスタ取得からプルダウン表示まで

シートの値を一度に読み込む

getActiveCustomers() は、getDataRange().getValues() で顧客マスタの値を二次元配列としてまとめて取得します。配列の values[0] が見出し行で、values.slice(1) が顧客データの行です。シートとのやり取りをまとめ、取得後の絞り込みや並べ替えはJavaScriptの配列処理で行います。Apps Script公式ガイドでも、読み書きの回数を抑え、データを配列にまとめて処理する方法が案内されています。

ヘッダー名から列の位置を調べる

createHeaderMap_(headers) は、1行目のヘッダー名をキー、列番号(0始まり)を値とするオブジェクトを作ります。たとえば、顧客コード が左から何列目にあっても、row[columnMap['顧客コード']] と書けば同じ列を参照できます。列の順番ではなくヘッダー名を手がかりにできるため、マスタの列を並べ替えてもコード側の列番号を書き換えずに済みます。

このコードは 顧客コード、顧客名、メールアドレス、表示順、有効区分 のヘッダー名を使います。列名を変更した場合は、コード側のキーと一致しなくなるため、シートとコードの両方を確認してください。このヘルパー関数は、第8回以降の一覧表示や検索でも再利用します。

有効な顧客を絞り、表示順に並べる

map() で各行を customerCode、customerName などの項目を持つオブジェクトに変換した後、filter() で必要な行だけ残します。条件は、顧客コードと顧客名が入力され、有効区分 が文字列 有効 と一致することです。有効 以外の行や、コード・顧客名が空の行はプルダウンに渡しません。

続く sort() は、表示順 の小さい順に顧客を並べ替えます。Number(row[...] || 0) により、表示順が空欄の場合は 0 として扱います。意図した順に表示するには、マスタの 表示順 に数値を入力してください。

最後の map() では、画面へ返す項目を顧客コード、顧客名、メールアドレスに絞り、メールアドレスが空欄なら空文字にします。画面の <option> では顧客コードを value に、顧客名を表示文字列と data-name に使います。このため、フォーム送信時には選択中の顧客コードと顧客名を saveRequest(data) に渡せます。

取得結果をプルダウンへ反映する

画面を開くと loadCustomers() が google.script.run から getActiveCustomers() を呼び出します。この呼び出しは非同期なので、ブラウザーはサーバー側の処理が終わるのを待たずに画面の処理を続けます。結果は withSuccessHandler() または withFailureHandler() に登録した関数で受け取ります。詳しくはHTML Serviceの通信に関する公式ガイドを参照してください。

成功時は renderCustomerOptions(customers) が <option> を作ります。返却値が空配列なら、シートがない、データ行がない、有効な顧客がない、または必要な列名が一致していない可能性があります。このとき選択欄を無効にし、マスタの確認を促します。一方、サーバー処理そのものが失敗した場合は handleCustomerLoadFailure(error) が呼ばれ、別のエラーメッセージを表示します。この二つを分けることで、マスタの内容確認が必要なのか、処理エラーの調査が必要なのかを判断しやすくなります。

loadCustomers(); は関数を定義するだけでは動きません。画面の初期化時にこの呼び出しを実行して、顧客取得処理を開始します。

8. 導入手順

  1. 対象のスプレッドシートから開いたApps Scriptプロジェクトに、第5回までのファイルを用意する
  2. Code.gs に getActiveCustomers() を追加する
  3. index.html の顧客プルダウンを読み込み中表示に変更する
  4. script.html に顧客マスタ読込処理を追加する(変数宣言・呼び出し・3つの関数)
  5. 保存する
  6. まだシートを作成していない場合は、第2回のコードで setupAppSheets() を実行し、顧客マスタ に5つのヘッダーとサンプル行があることを確認する
  7. Webアプリのテストデプロイを開き、画面を確認する

既存のWebアプリURLで変更を確認する場合は、変更を保存するだけではなく、デプロイを管理画面から新しいバージョンへ更新してください。Apps Scriptでは、デプロイが特定のバージョンを使う仕組みです。詳しい手順はApps Script公式ガイドと第4回のデプロイ手順を参照してください。

9. 動作確認

画面を開き、顧客プルダウンに 有効 な顧客だけが表示されることを確認します。

第2回のサンプルデータでは、サンプル商事 と テスト工業 が表示され、デモサービス は 無効 のため表示されません。

顧客プルダウンを開いた状態。選択してください、サンプル商事、テスト工業が表示されている

登録後、作業依頼 シートに顧客コードと顧客名が保存されていれば成功です。

作業依頼シートに登録された行。顧客コード、顧客名、件名、依頼内容、ステータス、担当者が保存されている

10. よくあるエラー

顧客が表示されない

顧客マスタ シートがあるか、1行目が 顧客コード、顧客名、メールアドレス、表示順、有効区分 になっているか確認してください。コードはヘッダー名をキーに列を参照するため、表記が一致しないと期待どおりに取得できません。

有効な顧客がないと表示される

有効区分 が 有効 になっている顧客があるか確認してください。

並び順が想定どおりにならない

表示順 に数値が入っているか確認してください。空欄は 0 として扱われるため、番号を付けて並べたい顧客には数値を入力します。

getSheetByName を呼び出す箇所でエラーになる

TypeError: Cannot read properties of null (reading 'getSheetByName') が出る場合は、SpreadsheetApp.getActiveSpreadsheet() が null を返しています。公式リファレンスにも、アクティブなスプレッドシートがない場合は null を返すと記載されています。このサンプルは対象のスプレッドシートに紐づいたApps Scriptプロジェクトを前提にしています。スプレッドシートを開き、メニューの「拡張機能」からApps Scriptを開いたプロジェクトにコードを配置しているか確認してください。単独のApps Scriptプロジェクトから使う場合は、対象ファイルをIDで開く処理が別途必要です。

11. starter.zipの案内

starter.zip には第5回完了時点のファイルを入れています。顧客選択肢はまだ固定のサンプル値なので、今回のコードを追加して差し替えてください。

starter.zipをダウンロード

12. complete.zipの案内

complete.zip には、Code.gs、index.html、style.html、script.html、README.txt を入れています。

complete.zipをダウンロード

13. 次回追加する機能

次回は、受付番号を自動採番します。今回の顧客マスタ連携は、作業依頼を登録する前に顧客コードと顧客名を選べるようにする処理です。次回は、その依頼データを識別する受付番号をサーバー側で作ります。

PropertiesService と LockService を使い、同時登録でも重複しにくい受付番号を作ります。




Your Message

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください。

スポンサードリンク

記事が気に入ったらシェアお願いします

PAGE TOP ↑