ドロップダウンの値を変更して別フィールドの値を変更したり、無効に設定する

目次

caution
警告

Moment.js はメンテナンスモードになり、 日付処理できる代替ライブラリへの移行 (External link) が推奨されています。
代替ライブラリのひとつ Luxon (External link) については、 kintone カスタマイズでの導入方法の紹介記事 があります。

概要

こちらのサンプルは、「ドロップダウン」フィールドの選択によって、別のフィールドの値を変更したり、入力を無効にするカスタマイズです。

また、「自動採番」「期間チェック」「任意の場所に外部リンクを表示させる」などの、便利な機能を実現させるサンプルも含まれています。

カスタマイズの適用方法は、「 適用手順」を参照してください。

完成形

デモ環境

cybozu developer network DemoSite (External link) のデモ環境で実際に動作を確認できます。
ログイン情報は cybozu developer network デモ環境 で確認してください。

準備

  • kintone アプリ(kintone アプリストアにある「 旅費精算申請 (External link) 」を使います)
    該当のアプリは、https://sample.cybozu.com/k/#/market/ から追加できます。
  • エディター
    エディターの準備については その2 エディタを準備する を参照してください。

適用手順

JavaScriptファイルの追加

  1. まずは、設定画面でカスタマイズに必要なライブラリを追加します。
    Moment.js:https://js.cybozu.com/momentjs/2.8.4/moment-with-locales.min.js
    アプリの設定画面から「JavaScript / CSS によるカスタマイズ」を開き、「PC 用の JavaScript ファイル」に、URL 指定で次のライブラリを順番に指定します。
    ライブラリの詳細に関しては、 Cybozu CDN を参照してください。

  2. 今回のカスタマイズの「サンプルプログラム」の追加もします。
    指定したライブラリの下に、次のように「サンプルプログラム」のファイルを追加します。

  3. サンプルプログラム 部分のコードをエディターにコピーして、ファイル名を「sample.js」、文字コードを「UTF-8」、BOM なしで保存します。
    ファイル名は任意ですが、ファイルの拡張子は「js」にしてください。

  4. 59 行目の https://example.com を、利用する路線検索サイトの URL に書き換えてください。

設定画面の完成イメージ

「JavaScript / CSS によるカスタマイズ」画面でそれぞれ設定した例を紹介します。

サンプルプログラム

サンプルプログラムでは、 Cybozu CDN の利用、JavaScript のサンプルを紹介します。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/*
 * 旅費精算申請のプログラム
 * Copyright (c) 2015 Cybozu
 *
 * Licensed under the MIT License
 */

(() => {

  'use strict';

  // 地域の条件に応じて日当を変更する。
  const changeArea = (event) => {
    const record = event.record;

    switch (record['地域'].value) {
      case '首都圏':
        record['日当'].value = '0';
        record['日当'].disabled = true;
        break;
      case '海外':
        record['日当'].value = '3000';
        record['日当'].disabled = true;
        break;
      case 'その他':
        record['日当'].disabled = false;
        break;
      default:
        record['日当'].value = '1000';
        record['日当'].disabled = true;
        break;
    }
  };

  // レコード追加、編集前、地域変更で処理を実行する
  const events = ['app.record.create.show', 'app.record.edit.show', 'app.record.index.edit.show',
    'app.record.edit.change.地域', 'app.record.create.change.地域', 'app.record.index.edit.change.地域'];
  kintone.events.on(events, (event) => {

    event.record.No.disabled = true;
    if (('app.record.create.show').indexOf(event.type) >= 0) {
      event.record.No.value = '';
    }

    // 地域変更による他のフィールド値変更
    changeArea(event);

    // 路線検索リンク表示
    const el = kintone.app.record.getSpaceElement('TravelSearch');
    // すでに設定されている子ノードがあったら削除する
    for (let i = el.childNodes.length - 1; i >= 0; i--) {
      el.removeChild(el.childNodes[i]);
    }

    const elTravel = document.createElement('a');
    elTravel.appendChild(document.createTextNode('路線検索サイト(外部サイト)'));
    // 路線検索サイトのリンクを指定します。
    elTravel.setAttribute('href', 'https://example.com');
    elTravel.setAttribute('target', '_blank');
    el.appendChild(elTravel);

    return event;
  });

  // 出発、帰着の値変更時のエラーチェックをする
  const eventCheck = ['app.record.create.change.出発', 'app.record.create.change.帰着',
    'app.record.edit.change.出発', 'app.record.edit.change.帰着',
    'app.record.index.edit.change.出発', 'app.record.index.edit.change.帰着'];
  kintone.events.on(eventCheck, (event) => {

    const record = event.record;
    const vlFrom = record['出発'].value;
    const vlTo = record['帰着'].value;

    // 出発が空白で、帰着が空白でない場合
    if (!(vlFrom) && (vlTo)) {
      record['出発'].error = '出発日を入力してください。';

    } else if ((vlFrom) && (vlTo)) {
      // 帰着が出発より前に設定されている場合
      if (vlFrom > vlTo) {
        record['帰着'].error = '出発日よりあとに変更してください。';
      }
    }
    return event;

  });

  // レコード追加、編集の保存前に自動採番を作成する
  const eventSubmit = ['app.record.create.submit', 'app.record.edit.submit', 'app.record.index.edit.submit'];
  kintone.events.on(eventSubmit, (event) => {

    // 保存前の画面上のレコード
    const record = event.record;
    const m = moment();

    // 旅費、経費が出発や帰着の範囲内でない場合はエラーを表示する
    const vlFrom = record['出発'].value;
    const vlTo = record['帰着'].value;

    // 旅費
    for (let i = 0; i < record['旅費'].value.length; i++) {
      const vlDate = record['旅費'].value[i].value['旅費日付'].value;

      if (!vlDate) {
        continue;
      }

      if ((vlDate < vlFrom) || (vlDate > vlTo)) {
        event.error = '旅費日付が申請期間の範囲外です。';
        record['旅費'].value[i].value['旅費日付'].error = '旅費日付が申請期間の範囲外です。';
        return event;
      }
    }

    // 経費
    for (let i = 0; i < record['経費'].value.length; i++) {
      const vlDate = record['経費'].value[i].value['経費日付'].value;

      if (!vlDate) {
        continue;
      }

      if ((vlDate < vlFrom) || (vlDate > vlTo)) {
        event.error = '経費日付が申請期間の範囲外です。';
        record['経費'].value[i].value['経費日付'].error = '経費日付が申請期間の範囲外です。';
        return event;
      }
    }

    // No が空白の場合に処理する。
    if (!record.No.value) {

      const appUrl = kintone.api.url('/k/v1/records', true) + '?app=' + kintone.app.getId() + '&query=' + encodeURI('limit 1&fields[0]=$id');
      const xmlHttp = new XMLHttpRequest();
      let recNo = 1;

      // 同期リクエストを行う
      xmlHttp.open('GET', appUrl, false);
      xmlHttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
      xmlHttp.send(null);

      if (xmlHttp.status === 200) {
        if (window.JSON) {
          const obj = JSON.parse(xmlHttp.responseText);
          if (obj.records[0] != null) {
            try {
              recNo = parseInt(obj.records[0].$id.value, 10) + 1;
            } catch (e) {
              event.error = '番号が取得できません。';
              return event;
            }
          }
          // 自動採番を見積番号に設定する
          const autoEstNo = 'T' + m.format('YYMMDD') + '-' + recNo;
          alert('番号 ' + autoEstNo + ' を登録します');
          event.record.No.value = autoEstNo;
        } else {
          event.error = xmlHttp.statusText;
        }
      } else {
        record.No.error = '番号が取得できません。';
      }
      return event;
    }

  });

  // レコード表示時の処理
  const eventShow = ['app.record.detail.show'];
  kintone.events.on(eventShow, (event) => {

    // 精算金額のスタイルを変更する
    const elTotal = kintone.app.record.getFieldElement('精算金額');
    elTotal.style.fontWeight = 'bold'; // 太字
    elTotal.style.fontSize = document.body.style.fontSize + 'large';

    // 印刷ボタンの表示
    const elPrint = document.createElement('button');
    elPrint.style.height = '30px';
    elPrint.style.width = '50px';
    elPrint.style.borderRadius = '0.5em';
    elPrint.appendChild(document.createTextNode('  印刷  '));
    elPrint.addEventListener('click', () => {
      alert('印刷するボタンの例です。\n実際には印刷されません。');
    }, false);

    kintone.app.record.getHeaderMenuSpaceElement().appendChild(elPrint);
  });

})();

使用したAPI