【Garoon JavaScript API】ワークフロー申請画面から kintone のレコードを検索してデータをセットする

目次

caution
警告

jQuery UI は v1.13 をもってメンテナンスモードになりました。
この記事で紹介しているカスタマイズでは、ダイアログの表示に jQuery UI を利用しています。
SweetAlert (External link) Micromodal (External link) などのダイアログを表示するその他のライブラリを使うよう、コードの書き換えを検討してください。

概要

次の記事で紹介した、Garoon と kintone の連携 Tips に続き、ワークフロー連携を紹介します。
「【Garoon JavaScript API】kintone の事前費用申請アプリから内容を引き継いでGaroonの支払申請を作成する」

今回は、kintone を開かずに、Garoon のワークフロー画面から kintone のデータを検索して、そのデータをセットするという内容です。
前回よりも、もう一歩踏み込んだ連携を紹介します!

前提条件と注意事項

  • このカスタマイズには、クラウド版 Garoon またはパッケージ版 Garoon 4.6 以降と、kintone のスタンダードコースの契約が必要です。
  • パッケージ版 Garoon のユーザーは、記事内容に追加してコードを修正する必要があります。
  • カスタマイズを実行するユーザーは、kintone を利用している必要があります。
  • ワークフロー JavaScript カスタマイズは、JavaScript を適用してから申請されたワークフローが対象になります。
    それ以前に申請されたワークフローには適用されません。

できること

kintone のアプリで日々利用した交通費を管理して Garoon でワークフロー申請する場合、kintone から Garoon へデータを連携します。
データの連携を kintone カスタマイズで実現すると、Garoon で申請を作りたいときに kintone の画面を開く必要があります。
この記事で紹介するカスタマイズでは、Garoon の画面から kintone のレコードを検索して、目的のレコードのデータをもとに申請を作成できます。

完成イメージ

kintone の「交通費申請」アプリに日々の交通費を登録し、月末に Garoon の「交通費申請」ワークフローで申請する場合の例です。

「申請者(検索用)」項目で名前を入力して、「kintone 検索」をクリックすると、kintone のレコードが検索され、Garoon へ設定されるという流れになります。

今回は kintone のアプリには JavaScript/CSS ファイルの設定は不要です。
Garoon のワークフローの設定にのみ、JavaScript/CSS ファイルを設定しカスタマイズしていきます。
kintone アプリのフィールドコードは利用するため、まずは kintone アプリの設定からはじめます。

kintone アプリの設定手順

交通費申請アプリを作成する

最初は kintone の交通費申請アプリを作成します。
完成すると、作成画面は次のようになります。
アプリストアの「交通費申請」アプリを修正していただくと、簡単に作れます。
アプリの具体的な作成方法は、以下のヘルプを参照してください。
アプリをはじめから作成する (External link)

フィールドは以下のとおり設定してください。
フィールドコードは、Garoon 側に設定する JavaScript コード内でそれぞれのフィールドを指定するための一意の文字列となります。
間違えないように設定してください。

フィールド名 フィールドコード フィールドタイプ 備考
申請者 Author 文字列(一行) 元の申請者フィールドを削除して文字列(一行)フィールドを追加します。
申請月 ApplicationMonth 文字列(一行) 文字列(一行)フィールドを追加します。
社員番号 EmployeeNumber 数値
所属部署 Department ドロップダウン
承認者 Authorizer ユーザー選択
タイトル Title 文字列(一行)
日付(テーブル) Date
日付 「日付」から「金額」まではテーブルとして設定します。
訪問先(テーブル) Destination 文字列(一行)
交通手段(テーブル) Transportation ドロップダウン ドロップダウンの項目を削除し、次の値を設定します。
  • 電車
  • バス
  • タクシー
  • 航空機
  • フェリー
  • その他
適用(テーブル) Application ラジオボタン ラジオボタンフィールドを追加し、項目には次の値を設定します。
  • 片道
  • 往復
金額(テーブル) Amount 数値
(テーブルの設定) LineItem テーブル テーブルのフィールドコードです。
合計金額 Total 計算
備考 Remarks 文字列(複数行)

カスタマイズを実行するユーザーに対し、アプリのレコード閲覧権限を設定してください。
これで kintone アプリの設定は完了です!
Garoon 側で設定する JavaScript ファイルの中に kintone のアプリ ID を入力しますので、アプリ ID は覚えておいてください。

例)https://xxxxx.cybozu.com/k/xxx/ ←アプリ ID はこの太字の部分に書いてある数字です。

Garoon ワークフローの設定手順

ワークフローの項目の内容は、会社さんによって異なります。
ここでは、完成イメージで利用した支払申請の申請フォームに JavaScript/CSS カスタマイズを設定する流れを説明します。

①Garoon ワークフローの申請フォームを作成する

まずは kintone でアプリを準備したのと同じく、Garoon で以下の項目を配置して、支払申請ワークフローの申請フォームを作成していきます。
申請フォームの作成方法については、Garoon ヘルプ - 申請フォームの作成の流れ クラウド版 (External link) パッケージ版 (External link) を参照してください。
フォーム作成は少し手間がかかるので、今回はそのまま読み込んで使えるサンプルフォームもご用意しています。(後述)

それぞれの項目は以下のとおり設定してください。
ここでも項目コードは、JavaScript コード内でそれぞれの項目を指定するための一意の文字列になります。
項目数が多いですが、こちらも間違えないように設定してください。

項目名 項目タイプ 項目コード 備考
標題 文字列(1行)(標準項目) Title 必須項目に設定
申請者(検索用) 文字列(1行) ApplicantForSearch
(JavaScriptカスタマイズ用項目) JavaScriptカスタマイズ用 AddButton
所属組織 メニュー
(空白行) (空白行) 「空白行を追加する」で設定
1. 日付 SUB1_ITEM1_1
行き先 文字列(1行) SUB1_ITEM1_2
詳細 メニュー SUB1_ITEM1_3
適用 ラジオボタン SUB1_ITEM1_4
金額 数値 SUB1_ITEM1_5
(空白行) (空白行) 「空白行を追加する」で設定
2. 日付 SUB1_ITEM2_1
行き先 文字列(1行) SUB1_ITEM2_2
詳細 メニュー SUB1_ITEM2_3
適用 ラジオボタン SUB1_ITEM2_4
金額 数値 SUB1_ITEM2_5
(空白行) (空白行) 「空白行を追加する」で設定
3. 日付 SUB1_ITEM3_1
行き先 文字列(1行) SUB1_ITEM3_2
詳細 メニュー SUB1_ITEM3_3
適用 ラジオボタン SUB1_ITEM3_4
金額 数値 SUB1_ITEM3_5
(空白行) (空白行) 「空白行を追加する」で設定
4. 日付 SUB1_ITEM4_1
行き先 文字列(1行) SUB1_ITEM4_2
詳細 メニュー SUB1_ITEM4_3
適用 ラジオボタン SUB1_ITEM4_4
金額 数値 SUB1_ITEM4_5
(空白行) (空白行) 「空白行を追加する」で設定
5. 日付 SUB1_ITEM5_1
行き先 文字列(1行) SUB1_ITEM5_2
詳細 メニュー SUB1_ITEM5_3
適用 ラジオボタン SUB1_ITEM5_4
金額 数値 SUB1_ITEM5_5
(空白行) (空白行) 「空白行を追加する」で設定
合計金額 自動計算
(空白行) (空白行) 「空白行を追加する」で設定
備考 文字列(複数行) Remarks

上記のとおり設定が完了したら、土台となる申請フォームの作成は完了です。

サンプルフォームのダウンロードについて

申請フォームを作成するのに少し時間がかかるので、まずは動きを見てみたいという方向けに、そのまま環境へ読み込んで使っていただけるサンプルの申請フォームを XML 形式でご用意しました。
完成イメージにある支払申請の申請フォームです。
以下のリンクを右クリックして、コンテキストメニューから[リンク先を別名で保存]をクリックしてダウンロードしてください。

Grkin_sample_form.xml

この XML ファイルを、「申請フォーム一覧」から読み込んでいただくと、「【サンプル】交通費申請」という申請フォームが追加されます。
項目コードも設定済みの状態です。
サンプルフォームを追加する方法については、 XMLファイルから読み込む (External link) を参照してください。
経路情報は各環境で異なり、存在しない経路を読み込むことはできないので、上記の XML では経路情報を省いています。
申請フォームを有効化するには、経路情報の設定が必要になりますので、そちらも忘れず設定してください。

②JavaScript/CSSファイルを適用する

申請フォームの作成が完了したので、あと一歩です。
ここから作成した申請フォームに JavaScript ファイルを適用していきます。
設定する JavaScript/CSS ファイルが少し多いですが、頑張ってください!

適用ファイルの準備

下記 3 つのサンプルコードをコピーして PC に保存します。

  1. grkin_common.js
    次のサンプルコードをエディタにコピーして、ファイル名を「grkin_common.js」、文字コードを「UTF-8」で保存します。
    ファイル名は任意ですが、ファイルの拡張子は「js」にしてください。
    16 行目には、kintone アプリの ID を入力してください。

     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
    
    /**
     * Garoon JavaScript APIを使ったサンプルプログラム
    * 「grkin_common.js」ファイル
    * Copyright (c) 2017 Cybozu
    *
    * Licensed under the MIT License
    * https://opensource.org/license/mit/
    */
    
    (function() {
      'use strict';
    
      window.garoonWFkintone = {
        appId: {
        // 1 は kintoneのアプリ ID に差し替える
          kintone: 1
        },
        fieldCode: {
          search: {
            // Garoonワークフローの「申請者(検索用)」項目の項目コード
            garoon: 'ApplicantForSearch',
            // kintoneアプリの「申請者」項目のフィールドコード
            kintone: 'Author'
          },
          selectItems: {
            // Garoonワークフロー申請画面で、申請者(検索用)項目に表示している [kintone検索]
            // をクリックした時に表示される、「kintone検索結果画面」に表示する項目
            // 例)申請者、タイトル、申請月
            kintone: ['Author', 'Title', 'ApplicationMonth']
          }
        },
        table: { // kintoneアプリのサブテーブルを連携対象とする場合に記入する場所
          kintone: {
            // テーブルのフィールドコードと、テーブルに含まれている項目のフィールドコードを記入する
            LineItem: ['Date', 'Destination', 'Transportation', 'Application', 'Amount']
          },
          garoon: {
            // Garoonワークフローの項目コード:[最大の行数, 最大の列数]
            // kintoneアプリのテーブルを連携対象としない場合は下記の1行を削除
            SUB1: [5, 5]
          }
        },
        relation: { // 関連付けの設定
          fields: {
            // 左側がガルーンの項目コード:右側がkintoneのフィールドコード
            Title: 'Title',
            Remarks: 'Remarks'
          },
          subTable: {
            // 左側がkintoneのフィールドコード:右側がガルーンの項目コード
            LineItem: 'SUB1'
          }
        }
      };
    })();
  2. grkin.js
    次のサンプルコードも同様にエディタにコピーして保存します。ファイル名を「grkin.js」、文字コードを「UTF-8」で保存してください。
    ファイル名は任意ですが、ファイルの拡張子は「js」にしてください。

      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
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    474
    475
    476
    477
    478
    479
    480
    481
    482
    483
    484
    485
    486
    487
    488
    489
    490
    491
    492
    493
    494
    495
    496
    497
    498
    499
    500
    501
    502
    503
    504
    505
    506
    507
    508
    509
    510
    511
    512
    513
    514
    515
    516
    517
    518
    519
    520
    521
    522
    523
    524
    525
    526
    527
    528
    529
    530
    531
    532
    533
    534
    535
    536
    537
    538
    539
    540
    541
    542
    543
    544
    545
    546
    547
    548
    549
    550
    551
    552
    553
    554
    555
    556
    557
    558
    559
    560
    561
    562
    563
    564
    565
    566
    567
    568
    569
    570
    571
    572
    573
    574
    575
    576
    577
    578
    579
    580
    581
    582
    583
    584
    585
    586
    587
    588
    589
    590
    591
    592
    593
    594
    595
    596
    597
    598
    599
    600
    601
    602
    603
    604
    605
    606
    607
    608
    609
    610
    611
    612
    613
    614
    615
    616
    617
    618
    619
    620
    621
    622
    623
    624
    625
    626
    627
    628
    629
    630
    631
    632
    633
    634
    635
    636
    637
    638
    639
    640
    641
    642
    643
    644
    645
    646
    647
    648
    649
    650
    651
    652
    653
    654
    655
    656
    657
    658
    659
    660
    661
    662
    663
    664
    665
    666
    667
    668
    669
    670
    671
    672
    673
    674
    675
    676
    677
    678
    679
    680
    681
    682
    683
    684
    685
    686
    687
    688
    689
    690
    691
    692
    693
    694
    695
    696
    697
    698
    699
    700
    701
    702
    703
    704
    705
    706
    707
    708
    709
    710
    711
    712
    713
    714
    715
    716
    717
    718
    719
    720
    721
    722
    723
    724
    725
    726
    727
    728
    729
    730
    731
    732
    733
    734
    735
    736
    737
    738
    739
    740
    741
    742
    743
    744
    745
    746
    747
    748
    749
    750
    751
    752
    753
    754
    755
    756
    757
    758
    759
    760
    761
    762
    763
    764
    765
    766
    767
    768
    769
    770
    771
    772
    773
    774
    775
    776
    777
    778
    779
    780
    781
    782
    783
    784
    785
    786
    787
    788
    789
    790
    791
    792
    793
    794
    795
    796
    797
    798
    799
    800
    801
    802
    803
    804
    805
    806
    807
    808
    809
    810
    811
    812
    813
    814
    815
    816
    817
    818
    819
    820
    821
    822
    823
    824
    825
    826
    827
    828
    829
    830
    831
    832
    833
    834
    835
    836
    837
    838
    839
    840
    841
    842
    843
    844
    845
    846
    847
    848
    849
    850
    851
    852
    853
    854
    855
    856
    857
    858
    859
    860
    861
    862
    863
    864
    865
    866
    867
    868
    869
    870
    871
    872
    873
    874
    875
    876
    877
    878
    879
    880
    881
    882
    883
    884
    885
    886
    887
    888
    889
    890
    891
    892
    893
    894
    895
    896
    897
    898
    899
    900
    901
    902
    903
    904
    905
    906
    907
    908
    909
    910
    911
    912
    913
    914
    915
    916
    917
    918
    919
    920
    921
    922
    923
    924
    925
    926
    927
    928
    929
    930
    931
    932
    933
    934
    935
    936
    937
    938
    939
    940
    941
    942
    943
    944
    945
    946
    947
    948
    949
    950
    951
    952
    953
    
    /**
     * Garoon JavaScript APIを使ったサンプルプログラム
     * 「grkin.js」ファイル
     * Copyright (c) 2017 Cybozu
     *
     * Licensed under the MIT License
     * https://opensource.org/license/mit/
     */
    
    (function($) {
      'use strict';
    
      // 「grkin_common.js」の値を取得
      const KINTONE_APP_ID = window.garoonWFkintone.appId.kintone;
      const GR_SEARCH_FIELD = window.garoonWFkintone.fieldCode.search.garoon;
      const KIN_SEARCH_FIELD = window.garoonWFkintone.fieldCode.search.kintone;
      const SELECT_ITEM = window.garoonWFkintone.fieldCode.selectItems.kintone;
      const GAROON_TABLE_INFO = window.garoonWFkintone.table.garoon;
      const KINTONE_TABLE_INFO = window.garoonWFkintone.table.kintone;
      const FIELD_RELATION = window.garoonWFkintone.relation.fields;
      const SUBTABLE_RELATION = window.garoonWFkintone.relation.subTable;
    
      // 空の配列を作成
      const FIELD_TYPE_INFO = [];
      const FIELD_LABEL_INFO = [];
    
      // Garoonのワークフローで表示している独自のボタンの文言
      const GR_BUTTON_SEARCH_ON_KINTONE = 'kintone検索';
      const DIALOG_BUTTON_CANCEL = 'キャンセル';
      const DIALOG_BUTTON_OK = 'OK';
      const DIALOG_BUTTON_SELECT = '選択';
    
      // [kintone検索結果]ダイアログで利用している文言
      const DIALOG_MESSAGE_NODATA = 'データがありません。選択された項目はクリアされます。';
      const DIALOG_MESSAGE_SUB_TOP = 'フォームに取り込む値を選択してください。';
      const DIALOG_MESSAGE_TOP = 'kintone検索結果';
      const DIALOG_MESSAGE_COUNT = '件';
    
      // エラーメッセージ
      const ERROR_MESSAGE_SEARCH_ON_KINTONE = 'kintoneデータの取得に失敗しました。';
      const ERROR_MESSAGE_GET_FORM_IFNO = 'form情報の取得に失敗しました。';
      const ERROR_MESSAGE_SETTING_NOT_MATCHED = 'kintoneのサブテーブルとGaroon側の設定が合っていません。';
      const ERROR_MESSAGE_NO_BUTTON = 'kintone検索用のボタンが無いか\nまたはフィールドコードが誤っています。';
      const ERROR_MESSAGE_NO_SEARCH_FIELD = 'kintone検索文字列用のフィールドが無いか\nまたはフィールドコードが誤っています。';
    
      // 空のオブジェクトを作成
      let RADIO_VALUES = {};
    
      /**
     * @param {string} htmlStr
     * @return {string} htmlStr
     */
      function escapeHtml(htmlStr) {
    
        if (htmlStr === null) {
          return '';
        }
    
        try {
          return htmlStr.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;').replace(/'/g, '&#39;').replace(',', '%,');
        } catch (e) {
          return htmlStr;
        }
      }
    
    
      /**
     * @param {string} str
     * @return {string} str
     */
      function escapeCanma(str) {
    
        if (str === null) {
          return '';
        }
    
        try {
          return str.replace('%', '%c').replace(',', '%d');
        } catch (e) {
          return str;
        }
      }
    
      /**
     * @param {string} grField
     * @param {string} value
     */
      function setRadioItem(grField, value) {
        $('.js_customization_input_item_' + grField).find('input').val([value]);
      }
    
      const Spin = {
        spinner: new Spinner({
          lines: 13,
          length: 28,
          width: 14,
          radius: 42,
          scale: 1,
          corners: 1,
          color: '#FFF',
          opacity: 0.25,
          rotate: 0,
          direction: 1,
          speed: 1,
          trail: 60,
          fps: 20,
          zIndex: 2e9,
          className: 'spinner',
          top: '50%',
          left: '50%',
          shadow: false,
          hwaccel: false,
          position: 'fixed'
        }),
    
        showSpinner: function() {
    
          if ($('.kintone-spinner').length === 0) {
            const spinBgDiv = $('<div id ="kintone-spin-bg" class="kintone-spinner"></div>');
            $(document.body).append(spinBgDiv);
          }
    
          $('.kintone-spinner').show();
          this.spinner.spin($('html')[0]);
        },
    
        hideSpinner: function() {
          $('.kintone-spinner').hide();
          this.spinner.stop();
        }
      };
    
    
      const SettingGaroonData = {
    
        /**
         * Set data in Garoon
         * @param {object} data
         * @param {object} request
         */
        setGaroonData: function(data, request) {
          let req = request;
    
          RADIO_VALUES = {};
    
          for (const grField in FIELD_RELATION) {
            if (!Object.prototype.hasOwnProperty.call(FIELD_RELATION, grField)) {
              continue;
            }
            const kinField = FIELD_RELATION[grField];
            const fieldType = FIELD_TYPE_INFO[kinField];
            const kinData = data[kinField].value;
    
            req = SettingGaroonData.setItem(grField, fieldType, kinData, req);
          }
    
          if (!$.isEmptyObject(GAROON_TABLE_INFO) && !$.isEmptyObject(KINTONE_TABLE_INFO)) {
            req = SettingGaroonData.setTableData(data, req);
          }
    
          garoon.workflow.request.set(req);
    
          for (const key in RADIO_VALUES) {
            if (!Object.prototype.hasOwnProperty.call(RADIO_VALUES, key)) {
              setRadioItem(key, RADIO_VALUES[key]);
            }
          }
        },
    
        /**
         * Set data to Garoon from the dialog
         * @param {object} data
         * @param {object} request
         */
        setGaroonDataForDialog: function(data, request) {
          let req = request;
    
          RADIO_VALUES = {};
    
          for (const grField in FIELD_RELATION) {
    
            if (!Object.prototype.hasOwnProperty.call(FIELD_RELATION, grField)) {
              continue;
            }
    
            const kinField = FIELD_RELATION[grField];
            const kinData = data[kinField];
            const fieldType = FIELD_TYPE_INFO[kinField];
    
            if (!Object.prototype.hasOwnProperty.call(data, kinField)) {
              continue;
            }
    
            req = SettingGaroonData.setItem(grField, fieldType, kinData, req);
          }
    
          if (!$.isEmptyObject(GAROON_TABLE_INFO) && !$.isEmptyObject(KINTONE_TABLE_INFO)) {
            req = SettingGaroonData.setTableDataForDialog(data, req);
          }
    
          garoon.workflow.request.set(req);
    
          for (const key in RADIO_VALUES) {
            if (Object.prototype.hasOwnProperty.call(RADIO_VALUES, key)) {
              setRadioItem(key, RADIO_VALUES[key]);
            }
          }
        },
    
        /**
         * Set sub table data in Garoon
         * @param {object} data
         * @param {object} request
         * @return {object} req
         */
        setTableData: function(data, request) {
          let req = request;
    
          for (const tableCode in KINTONE_TABLE_INFO) {
    
            if (!Object.prototype.hasOwnProperty.call(KINTONE_TABLE_INFO, tableCode)) {
              continue;
            }
    
            const kinTableData = data[tableCode].value;
            const kinFieldTypes = KINTONE_TABLE_INFO[tableCode];
            const grTableCode = SUBTABLE_RELATION[tableCode];
            const maxRow = GAROON_TABLE_INFO[grTableCode][0];
            const maxColumn = GAROON_TABLE_INFO[grTableCode][1];
            const rowIdx = 1;
    
            if (maxRow < kinTableData.length || maxColumn < kinFieldTypes.length) {
              swal('Error!', ERROR_MESSAGE_SETTING_NOT_MATCHED, 'error');
              return req;
            }
            req = SettingGaroonData.clearSubtableData(grTableCode, kinFieldTypes, maxRow, maxColumn, req);
            req = SettingGaroonData.loopSubtableData(kinTableData, kinFieldTypes, grTableCode, rowIdx, req);
          }
    
          return req;
        },
    
        /**
         * Loop processing of sub table data
         * @param {object} kinTableData
         * @param {string} kinFieldTypes
         * @param {string} grTableCode
         * @param {number} rowNum
         * @param {object} request
         * @return {object} req
         */
        loopSubtableData: function(kinTableData, kinFieldTypes, grTableCode, rowNum, request) {
          let req = request;
          let rowIdx = rowNum;
    
          for (let i = 0; i < kinTableData.length; i++) {
            const tableValue = kinTableData[i].value;
            let columIdx = 1;
    
            for (let j = 0; j < kinFieldTypes.length; j++) {
              const fieldValue = tableValue[kinFieldTypes[j]].value;
              const fieldType = tableValue[kinFieldTypes[j]].type;
              const grFieldCode = `${grTableCode}_ITEM${rowIdx}_${columIdx}`;
    
              req = SettingGaroonData.setItem(grFieldCode, fieldType, fieldValue, req);
    
              if (fieldType !== 'RADIO_BUTTON') {
                req = SettingGaroonData.setItem(grFieldCode, fieldType, fieldValue, req);
              } else {
                RADIO_VALUES[grFieldCode] = fieldValue;
              }
    
              columIdx++;
            }
            rowIdx++;
          }
          return req;
        },
    
        /**
             * Set sub table data in Garoon from the dialog
             * @param {object} data
             * @param {object} request
             * @return {object} req
             */
        setTableDataForDialog: function(data, request) {
          let req = request;
    
          for (const key in KINTONE_TABLE_INFO) {
    
            if (!Object.prototype.hasOwnProperty.call(KINTONE_TABLE_INFO, key)) {
              continue;
            }
    
            const kinTableData = data[key];
            const fieldTypes = KINTONE_TABLE_INFO[key];
            const grField = SUBTABLE_RELATION[key];
            const maxRow = GAROON_TABLE_INFO[grField][0];
            const maxColumn = GAROON_TABLE_INFO[grField][1];
            const rowIdx = 1;
    
            if (maxRow < kinTableData.length || maxColumn < fieldTypes.length) {
              swal('Error!', ERROR_MESSAGE_SETTING_NOT_MATCHED, 'error');
              return req;
            }
            req = SettingGaroonData.clearSubtableData(grField, fieldTypes, maxRow, maxColumn, req);
            req = SettingGaroonData.loopSubtableDataForDaialog(kinTableData, fieldTypes, grField, rowIdx, req);
          }
          return req;
        },
    
        /**
             * Loop processing of sub table data from the dialog
             * @param {object} kinTableData
             * @param {string} fieldTypes
             * @param {string} grField
             * @param {number} rowNum
             * @param {object} request
             * @return {object} req
             */
        loopSubtableDataForDaialog: function(kinTableData, fieldTypes, grField, rowNum, request) {
          let req = request;
          let rowIdx = rowNum;
    
          for (let i = 0; i < kinTableData.length; i++) {
    
            if (kinTableData[i] === undefined) {
              continue;
            }
    
            const fieldData = kinTableData[i].split(',');
            let columIdx = 1;
    
            for (let j = 0; j < fieldData.length; j++) {
              const fieldType = FIELD_TYPE_INFO[fieldTypes[j]];
              const kinData = (fieldData[j]).replace('%c', '%').replace('%d', ',');
              const grCode = `${grField}_ITEM${rowIdx}_${columIdx}`;
    
              if (fieldType !== 'RADIO_BUTTON') {
                req = SettingGaroonData.setItem(grCode, fieldType, kinData, req);
              } else {
                RADIO_VALUES[grCode] = kinData;
              }
              columIdx++;
            }
            rowIdx++;
          }
          return req;
        },
    
        /**
             * Clear sub table data set on Garoon
             * @param {string} grField
             * @param {string} fieldTypes
             * @param {number} maxRow
             * @param {number} maxColumn
             * @param {object} req
             * @return {object} req
             */
        clearSubtableData: function(grField, fieldTypes, maxRow, maxColumn, req) {
    
          for (let j = 1; j <= maxRow; j++) {
    
            for (let k = 1; k <= maxColumn; k++) {
              const fieldType = FIELD_TYPE_INFO[fieldTypes[k - 1]];
              const grCode = `${grField}_ITEM${j}_${k}`;
    
              switch (fieldType) {
    
                case 'DATE':
                  req.items[grCode].value = null;
                  break;
    
                case 'DATETIME':
                  req.items[grCode].value = null;
                  break;
    
                default:
                  req.items[grCode].value = '';
                  break;
              }
            }
          }
          return req;
        },
    
        /**
             * Set the value
             * @param {string} grField
             * @param {string} fieldTypes
             * @param {object} kinData
             * @param {object} req
             * @return {object} req
             */
        setItem: function(grField, fieldType, kinData, req) {
    
          switch (fieldType) {
    
            case 'DATE': {
              if (kinData === 'null') {
                break;
              }
    
              req.items[grField].value = kinData;
              break;
            }
            case 'DATETIME': {
              if (kinData === '') {
                break;
              }
    
              const d = luxon.DateTime.fromISO(kinData).toFormat('yyyy-MM-dd');
              const t = luxon.DateTime.fromISO(kinData).toFormat('HH:mm');
              req.items[grField].value = {
                date: d,
                time: t
              };
              break;
            }
            case 'DROP_DOWN': {
              if (kinData === 'null') {
                break;
              }
    
              req.items[grField].value = kinData;
              break;
            }
            case 'RADIO_BUTTON': {
              RADIO_VALUES[grField] = kinData;
              break;
            }
            default: {
              req.items[grField].value = kinData;
              break;
            }
          }
          return req;
        }
      };
    
      /**
     * If there is no search data, clear the setting value for sub data
     * @param {object} request
     * @return {object} request
     */
      function clearSubFieldData(request) {
        let req = request;
        for (const key in KINTONE_TABLE_INFO) {
    
          if (!Object.prototype.hasOwnProperty.call(KINTONE_TABLE_INFO, key)) {
            continue;
          }
    
          const fieldTypes = KINTONE_TABLE_INFO[key];
          const grField = SUBTABLE_RELATION[key];
          const maxRow = GAROON_TABLE_INFO[grField][0];
          const maxColumn = GAROON_TABLE_INFO[grField][1];
    
          req = SettingGaroonData.clearSubtableData(grField, fieldTypes, maxRow, maxColumn, req);
        }
        return req;
      }
    
    
      /**
     * If there is no search data, clear the setting value
     * @param {object} request
     * @return {object} request
     */
      function clearFiledData(request) {
    
        let req = request;
        req.items[GR_SEARCH_FIELD].value = '';
    
        for (const grField in FIELD_RELATION) {
          if (!Object.prototype.hasOwnProperty.call(FIELD_RELATION, grField)) {
            continue;
          }
    
          const kinField = FIELD_RELATION[grField];
          const fieldType = FIELD_TYPE_INFO[kinField];
    
          switch (fieldType) {
    
            case 'DATE':
              req.items[grField].value = null;
              break;
    
            case 'DATETIME':
              req.items[grField].value = null;
              break;
    
            default:
              req.items[grField].value = '';
              break;
          }
    
          if (!$.isEmptyObject(GAROON_TABLE_INFO) && !$.isEmptyObject(KINTONE_TABLE_INFO)) {
            req = clearSubFieldData(request);
          }
        }
        return req;
      }
    
    
      /**
         * Dialog function
         */
      const SelectDialog = {
    
        /**
         * get values from the dialog
         * @param {object} el
         * @return {object} dataInfo
         */
        getDialogData: function(el) {
          const dataInfo = [];
    
          for (const key in FIELD_RELATION) {
    
            if (!Object.prototype.hasOwnProperty.call(FIELD_RELATION, key)) {
              continue;
            }
    
            const kinField = FIELD_RELATION[key];
            dataInfo[kinField] = el.find('#' + kinField).val();
          }
    
          if (!$.isEmptyObject(GAROON_TABLE_INFO) && !$.isEmptyObject(KINTONE_TABLE_INFO)) {
    
            for (const tableCode in KINTONE_TABLE_INFO) {
    
              if (!Object.prototype.hasOwnProperty.call(KINTONE_TABLE_INFO, tableCode)) {
                continue;
              }
    
              const valueArr = [];
              const num = $(el).find('input[id^=' + tableCode + '_]').length;
    
              for (let i = 0; i < num; i++) {
                valueArr.push(el.find('#' + tableCode + '_' + i).val());
              }
    
              dataInfo[tableCode] = valueArr;
            }
          }
          return dataInfo;
        },
    
        /**
             * Display data in dialog
             * @param {string} dataHtml
             * @param {object} request
             */
        showDialog: function(dataHtml, request, dataFlag) {
    
          let req = request;
          let buttonName;
    
          const $dateDialog = $('<div>');
          $dateDialog.attr('id', 'kintone-dialog');
          $dateDialog.html(dataHtml);
    
          if (dataFlag === 1) {
            buttonName = DIALOG_BUTTON_CANCEL;
          } else {
            buttonName = DIALOG_BUTTON_OK;
          }
    
          const button = {};
          button[buttonName] = function() {
            $(this).dialog('close');
            $(this).remove();
          };
    
          $dateDialog.dialog({
            title: DIALOG_MESSAGE_TOP,
            autoOpen: false,
            width: 'auto',
            maxHeight: 700,
            show: 400,
            hide: 400,
            modal: true,
            buttons: button,
    
            close: function(event, ui) {
              if (buttonName === DIALOG_BUTTON_OK) {
                req = clearFiledData(request);
                garoon.workflow.request.set(req);
              }
            }
          });
    
          $('#kintone-dialog').dialog('open');
    
          $('.select_btn').click(function() {
            const params = SelectDialog.getDialogData($(this).parents('.kintone-select-tr'));
    
            SettingGaroonData.setGaroonDataForDialog(params, request);
    
            $('#kintone-dialog').dialog('close');
            $('#kintone-dialog').remove();
          });
        }
      };
    
    
      const CreateHtml = {
    
        /**
         * Create a selection list for dialog display
         * @param {object} kinRec
         * @return {string} result
         */
        createSelectList: function(kinRec) {
          let datalist = '';
          let count = 0;
    
          for (let i = 0; i < kinRec.records.length; i++) {
            const kintoneRecord = kinRec.records[i];
            datalist +=
                        '<tr id="selectlist_' + i + '" class="kintone-select-tr">' +
                        '<td class="select-cell-kintone">' +
                        '<span><button class="button-simple-custom select_btn" type="button">' + DIALOG_BUTTON_SELECT +
                            '</button></span></td>' +
                        CreateHtml.createSelectData(kintoneRecord) +
                        CreateHtml.createInputData(kintoneRecord) +
                    '</tr>';
    
            count++;
          }
    
          const result =
                    '<span class="selectexternalItemWindow_text">' + DIALOG_MESSAGE_SUB_TOP + '</span>' +
                    '<table class="listTable-kintone select-table-kintone">' +
                        '<thead class="select-thead-gaia">' +
                            '<tr>' +
                                '<th><div><span class="recordlist-header-label-kintone">' +
                                count + DIALOG_MESSAGE_COUNT + '</span></div></th>' +
                                CreateHtml.createHeading() +
                            '</tr>' +
                        '</thead><tbody>' + datalist + '</tbody>' +
                    '</table>';
    
          return result;
        },
    
        /**
             * Create header section
             * @return {string} headingData
             */
        createHeading: function() {
          let headingData = '';
    
          for (let i = 0; i < SELECT_ITEM.length; i++) {
            const filedCode = SELECT_ITEM[i];
            const filedLabel = FIELD_LABEL_INFO[filedCode];
            headingData +=
                        '<th><div><span class="recordlist-header-label-kintone">' + filedLabel + '</span></div></th>';
          }
          return headingData;
        },
    
        /**
             * Create select section
             * @param {object} record
             * @return {string} selectData
             */
        createSelectData: function(record) {
          let selectData = '';
    
          for (let i = 0; i < SELECT_ITEM.length; i++) {
            const filedCode = SELECT_ITEM[i];
            selectData +=
                        '<td><div class="line-cell-kintone"><span>' + escapeHtml(record[filedCode].value) + '</span></div>';
          }
          return selectData;
        },
    
        /**
             * Create Input data section
             * @param {object} record
             * @return {string} inputData
             */
        createInputData: function(record) {
          let inputData = '';
    
          for (const key in FIELD_RELATION) {
    
            if (!Object.prototype.hasOwnProperty.call(FIELD_RELATION, key)) {
              continue;
            }
    
            const kinField = FIELD_RELATION[key];
            inputData +=
                        '<input id="' + kinField + '" value="' + escapeHtml(record[kinField].value) + '" type="hidden">';
          }
    
          if (!$.isEmptyObject(GAROON_TABLE_INFO) && !$.isEmptyObject(KINTONE_TABLE_INFO)) {
    
            for (const tableCode in KINTONE_TABLE_INFO) {
    
              if (!Object.prototype.hasOwnProperty.call(KINTONE_TABLE_INFO, tableCode)) {
                continue;
              }
    
              const kinTableData = record[tableCode];
              const kinTableField = KINTONE_TABLE_INFO[tableCode];
    
              for (let j = 0; j < kinTableData.value.length; j++) {
                const rowData = kinTableData.value[j];
                const valueArr = [];
    
                for (let k = 0; k < kinTableField.length; k++) {
                  valueArr.push(escapeCanma(rowData.value[kinTableField[k]].value));
                }
    
                inputData +=
                                '<input id="' + tableCode + '_' + j + '" value="' + valueArr + '" type="hidden">';
              }
            }
          }
          return inputData;
        },
    
        /**
             * create NoData message
             * @return {string} nodataMsg
             */
        createNoData: function() {
          const nodataMsg = '<div id="kintone_lookup_validator_error" class="input-error-custom">' +
                        '<span>' + DIALOG_MESSAGE_NODATA + '</span></div>';
          return nodataMsg;
        }
      };
    
    
      const createFormInfo = {
    
        /**
         * Get form information on kintone
         * @param {object} record
         */
        getFormInfo: function(record) {
    
          $.ajax({
            type: 'GET',
            url: '/k/v1/app/form/fields.json',
            data: {app: KINTONE_APP_ID},
            cache: false,
            dataType: 'json'
          }).done((resp) => {
    
            for (const i in resp.properties) {
    
              if (Object.prototype.hasOwnProperty.call(resp.properties, i)) {
                FIELD_TYPE_INFO[[resp.properties[i].code]] = resp.properties[i].type;
                FIELD_LABEL_INFO[[resp.properties[i].code]] = resp.properties[i].label;
    
                if (resp.properties[i].type === 'SUBTABLE') {
                  const fields = resp.properties[i].fields;
    
                  for (const j in fields) {
                    // eslint-disable-next-line max-depth
                    if (!Object.prototype.hasOwnProperty.call(fields, j)) {
                      continue;
                    }
                    FIELD_TYPE_INFO[[fields[j].code]] = fields[j].type;
                    FIELD_LABEL_INFO[[fields[j].code]] = fields[j].label;
                  }
                }
              }
            }
          }).fail((error) => {
            Spin.hideSpinner();
            swal('Error!', ERROR_MESSAGE_GET_FORM_IFNO, 'error');
          });
        }
      };
    
    
      /**
     * Grkin cooperation execution
     */
      function execGrkin() {
    
        const request = garoon.workflow.request.get();
        if (request.items[GR_SEARCH_FIELD] === undefined) {
          swal('Error!', ERROR_MESSAGE_NO_SEARCH_FIELD, 'error');
          return;
        }
        Spin.showSpinner();
    
        const searchWord = request.items[GR_SEARCH_FIELD].value;
        let query = KIN_SEARCH_FIELD + ' like "' + searchWord + '"';
    
        if (!searchWord) {
          query = '';
        }
    
        $.ajax({
          type: 'GET',
          url: '/k/v1/records.json',
          data: {app: KINTONE_APP_ID, query: query},
          cache: false,
          dataType: 'json'
        }).done((resp) => {
          Spin.hideSpinner();
    
          let dataHtml;
    
          if (resp.records.length === 0) {
            dataHtml = CreateHtml.createNoData();
            SelectDialog.showDialog(dataHtml, request, 0);
    
          } else if (resp.records.length === 1) {
            SettingGaroonData.setGaroonData(resp.records[0], request);
    
          } else {
            dataHtml = CreateHtml.createSelectList(resp);
            SelectDialog.showDialog(dataHtml, request, 1);
          }
    
        }).fail((error) => {
          Spin.hideSpinner();
          swal('Error!', ERROR_MESSAGE_SEARCH_ON_KINTONE, 'error');
        });
      }
    
    
      /**
     * Disable editable fields for sub table
     * @param {string} key
     */
      function disableSubFiled(key) {
        const maxRow = GAROON_TABLE_INFO[key][0];
        const maxColumn = GAROON_TABLE_INFO[key][1];
    
        for (let j = 1; j <= maxRow; j++) {
    
          for (let k = 1; k <= maxColumn; k++) {
    
            if ($('.js_customization_input_item_' + key + '_ITEM' + j + '_' + k)[0].nodeName !== 'TD') {
              $('.js_customization_input_item_' + key + '_ITEM' + j + '_' + k)
                .css('background-color', 'rgb(235, 235, 228)')
                .on('keyup keydown keypress select click mousedown', (e) => {
                  e.preventDefault();
                });
    
            } else {
              $('.js_customization_input_item_' + key + '_ITEM' + j + '_' + k).children('select')
                .css('background-color', 'rgb(235, 235, 228)')
                .on('keyup keydown keypress select click mousedown', (e) => {
                  e.preventDefault();
                });
              $('.js_customization_input_item_' + key + '_ITEM' + j + '_' + k).children('a')
                .on('keyup keydown keypress select click mousedown', function(e) {
                  $(this).css('pointer-events', 'none');
                });
    
              $('.js_customization_input_item_' + key + '_ITEM' + j + '_' + k).children('input')
                .css('background-color', 'rgb(235, 235, 228)')
                .on('keyup keydown keypress select click mousedown', (e) => {
                  e.preventDefault();
                });
            }
          }
        }
      }
    
    
      /**
     * Disable editable fields
     */
      function disableFiled() {
    
        for (const grField in FIELD_RELATION) {
          if (!Object.prototype.hasOwnProperty.call(FIELD_RELATION, grField)) {
            continue;
          }
    
          if ($('.js_customization_input_item_' + grField)[0].nodeName.toLowerCase() !== 'td') {
            $('.js_customization_input_item_' + grField)
              .css('background-color', 'rgb(235, 235, 228)')
              .on('keyup keydown keypress select click mousedown', (e) => {
                e.preventDefault();
              });
          } else {
            $('.js_customization_input_item_' + grField).children('select')
              .css('background-color', 'rgb(235, 235, 228)')
              .on('keyup keydown keypress select click mousedown', (e) => {
                e.preventDefault();
              });
            $('.js_customization_input_item_' + grField).children('a')
              .on('keyup keydown keypress select click mousedown', function(e) {
                $(this).css('pointer-events', 'none');
              });
    
            $('.js_customization_input_item_' + grField).children('input')
              .css('background-color', 'rgb(235, 235, 228)')
              .on('keyup keydown keypress select click mousedown', (e) => {
                e.preventDefault();
              });
          }
    
          if (!$.isEmptyObject(GAROON_TABLE_INFO) && !$.isEmptyObject(KINTONE_TABLE_INFO)) {
            for (const key in GAROON_TABLE_INFO) {
              if (!Object.prototype.hasOwnProperty.call(GAROON_TABLE_INFO, key)) {
                continue;
              }
    
              disableSubFiled(key);
            }
          }
        }
      }
    
    
      /**
     * Application form additional event
     * @param {object} event
     * @return {object} event
     */
      garoon.events.on('workflow.request.create.show', (event) => {
    
        disableFiled();
    
        const space = garoon.workflow.request.getSpaceElement('AddButton');
        if (space === null) {
          swal('Error!', ERROR_MESSAGE_NO_BUTTON, 'error');
          return;
        }
    
        createFormInfo.getFormInfo();
    
        const addButtonEl = document.createElement('input');
        addButtonEl.type = 'button';
        addButtonEl.value = GR_BUTTON_SEARCH_ON_KINTONE;
        addButtonEl.id = 'addBtn';
        space.appendChild(addButtonEl);
    
        $('#addBtn').click(() => {
    
          $('#kintone-dialog').remove();
    
          execGrkin();
    
        });
        return event;
      });
    
    })(jQuery.noConflict(true));
  3. grkin.css
    次のサンプルコードも同様にエディタにコピーして保存します。ファイル名を「grkin.css」、文字コードを「UTF-8」で保存してください。
    ファイル名は任意ですが、ファイルの拡張子は「css」にしてください。

     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
    
    /**
     * Garoon JavaScript APIを使ったサンプルプログラム
     *
     * 「grkin.css」ファイル
     *
     * Copyright (c) 2017 Cybozu
     *
     * Licensed under the MIT License
     */
    .listTable-kintone.select-table-kintone {
      width: auto;
      table-layout: fixed;
      background-color: #fff;
      border-spacing: 0;
      font-size: 14px;
      margin-top: 13px;
    }
    
    .listTable-kintone.select-table-kintone th {
      width: auto;
      color: #333333;
      padding: 2px 10px;
      background-color: #9DBEC6;
      border: 1px solid #cccccc;
      line-height: 120%;
      text-align: center;
    }
    
    .listTable-kintone.select-table-kintone th:first-child {
      width: 80px;
      border-radius: 5px 0 0 0;
    }
    
    .listTable-kintone.select-table-kintone th:last-child {
      border-radius:0 5px 0 0;
      /*border-right:1px solid #258;*/
      border: 1px solid #cccccc;
      box-shadow: 2px 2px 1px rgba(0,0,0,0.1),0px 1px 1px rgba(255,255,255,0.3) inset;
    }
    
    .listTable-kintone.select-table-kintone tr td {
      text-align: center;
      border: 1px solid #cccccc;
    }
    
    .listTable-kintone.select-table-kintone tr td:last-child {
      /*border-right: 1px solid #84b2e0;*/
      border: 1px solid #cccccc;
      box-shadow: 2px 2px 1px rgba(0,0,0,0.1);
    }
    
    
    .listTable-kintone.select-table-kintone tr:last-child td {
      box-shadow: 2px 2px 1px rgba(0,0,0,0.1);
    }
    
    .listTable-kintone.select-table-kintone tr:last-child td:first-child {
      border-radius: 0 0 0 5px;
    }
    
    .listTable-kintone.select-table-kintone tr:last-child td:last-child {
      border-radius: 0 0 5px 0;
    }
    
    .listTable-kintone.select-table-kintone tbody tr:nth-child(2n) {
      /*background-color: #f7f9fa*/
      background-color: #E6F1F2;
    }
    
    .listTable-kintone.select-table-kintone td>div,.listTable-kintone.select-table-kintone th>div {
      /*padding: 16px 16px 8px*/
      padding: 5px 5px 1px
    }
    
    .listTable-kintone.select-table-kintone tbody tr:focus {
      background-color: #f5f5f5
    }
    
    .button-simple-custom {
      margin: 5px
    }
    
    
    #kintone-spin-bg {
      position: fixed;
      top: 0px;
      left: 0px;
      z-index: 500;
      width: 100%;
      height: 1000%;
      background-color: #000;
      opacity: 0.5;
      filter: alpha(opacity=50);
      -ms-filter: alpha(opacity=50);
    }
JavaScript/CSSファイルとして使用するファイルのおよびリンクの追加
  1. 「申請フォーム情報」部分の右端にある「JavaScript / CSS によるカスタマイズ」をクリックします。
    ワークフローのカスタマイズが許可されていない場合、申請フォームの詳細画面に「JavaScript / CSS によるカスタマイズ」リンクが表示されません。
    Garoon ヘルプ - ワークフローのカスタマイズを許可する クラウド版 (External link) パッケージ版 (External link) を参照してください。

  2. [カスタマイズ]項目に「適用する」を選択してください。
  3. [JavaScript カスタマイズ]項目に下記を設定します。
    1. Cybozu CDN の次のライブラリを使用しますので、次のリンクを追加してください。
      • jQuery
        https://js.cybozu.com/jquery/3.6.4/jquery.min.js
      • JQuery UI
        https://js.cybozu.com/jqueryui/1.13.2/jquery-ui.min.js
      • Luxon
        https://js.cybozu.com/luxon/3.3.0/luxon.min.js
      • Spin.js
        https://js.cybozu.com/spinjs/2.3.2/spin.min.js
      • SweetAlert
        https://js.cybozu.com/sweetalert/v1.1.3/sweetalert.min.js
    2. 「適用ファイルの準備」で用意した「grkin_common.js」と「grkin.js」のファイルを追加してください。
      設定するファイルおよびリンクの順番には意味がありますので、次の点をご注意ください。
      • 「grkin.js」のファイル内で、各ライブラリや「grkin_common.js」ファイルの内容を参照している為、各ライブラリや「grkin _common.js」ファイルを先に読み込まなければいけません。
        そのため、JavaScript カスタマイズの適用するファイルおよびリンクの順番は次のようにお願いします。
        設定する順番:
        1. 各ライブラリ
        2. grkin_common.js
        3. grkin.js
  4. [CSS カスタマイズ]項目には、下記を設定します。
    1. Cybozu CDN の次のライブラリを使用しますので、次のリンクを追加してください。
      • jQuery UI
        https://js.cybozu.com/jqueryui/1.13.2/themes/smoothness/jquery-ui.css
      • SweetAlert
        https://js.cybozu.com/sweetalert/v1.1.3/sweetalert.css
    2. 「適用ファイルの準備」で用意した「grkin.css」のファイルを追加してください。 こちらも設定する順番にはご注意ください。
  5. 上記の設定完了後の完成イメージはこちらです。
    同じように設定ができましたら、「設定」をクリックしてください。

以上ですべての設定は完了です!
最初にお見せした完成イメージのとおり、動けば成功です。
お疲れさまでした!

おわりに

今回のサンプルはいかがだったでしょうか?

「grkin.js」というファイルは、変更しないことを前提にサンプルを作成しており、そのファイルの内容は上級者向けです。
そのため、今までのサンプルにはない内容となっています。
エンジニアの方なら、こちらのファイルも修正してみようかな?と思う方もいらっしゃるかもしれません。

Garoon に JavaScript カスタマイズが追加されて、できることが広がっています。
このサンプルをきっかけにぜひ、いろいろと試していただければと思います。

今後もこのようなサンプルを紹介しますので、また見に来てください。