動的に2段の連動プルダウンメニューより取得した入力情報のアプリヘの取り込みについて

始めて質問させていただきます。JavaScriptの超初心者です。目下、下記課題に取り組んでいます。

各顧客に対して複数の施策予定項目A1~Anがあり、
それぞれの顧客を担当する担当者別に、項目A1~An別の実施予定件数の合計、A1~Anごとの実績件数の合計、
そして 項目A1~Anごとの進捗率を集計して、Kintone案件一覧表示の上部余白に表として
表示しようとしています。各担当者は複数の顧客を担当します。各担当者はそれぞれ部門に所属し、
部門としての集計も実施します。

集計の種類は(1)指定した特定担当者のみの集計、(2)所属担当者全員の小計を
表示したうえで、さらにその下に部門の集計表示する担当者小計付き部門合計、
(3)各部門の小計を個別に表示した後、さらにその下に全体の集計を表示する部門小計付き全体集計、
(4)全部門のみの集計の4種類の集計を表示することを目指しています。

現状、チュートリアルなどを参考に見よう見まねで、(1)~(4)について、どの集計をするかの
情報(部門名and/or担当者名)を指定をすると(1)~(4)のいずれも正しく表示することができる
ところまでJavaScriptを作成できました。

次のステップとして、(1)~(4)の種別と、(1)の場合は集計対象の担当者名、(2)の場合は
集計対象の部門名を、本アプリ使用者(担当者とは限らない)に2段の動的連動プルダウンで
部名と担当者名を選択して入力してもらい、その結果を上記作成済みのアプリに取り込み
その入力結果に応じて集計結果を表示させたいと考えています。表示は「カスタマイズ
レコード一覧の表示形式」です。formタグ、onChangeタグ等をいじって
トライしていますが、うまくプルダウンからの入力情報を上記アプリに渡せていない状況です。
   2段の連動プルダウンとしては、例えば下記サイトにあるようなイメージを想定しています。

http://phpjavascriptroom.com/?t=js&p=selectoption#a_pulldown

このサイトのソースを カスタマイズレコード一覧の表示形式のHTML設定部分に張り付つけても
<script>タグ以下が無効化されるためか、うまく動作しません。上記Webの記事のソースを単独で
実行すると狙い通りの動作をしますが、Kintoneに組み込もうとすると狙い通りの動作を
しません。どのようにすれば実現できるのか教示頂きたく、よろしくお願いします。

できましたら、HTML設定部での設定内容と、formからの入力情報を取り出して
上記集計アプリに連動させるjavaScriptのソースを提示頂けたらと思います。
なお、部名と担当者名の入力については、上記2段のプルダウンメニュ
方式にはこだわりません。より簡潔でスマートな方法で使用者の選択内容を取得する
ことができればそれでも結構です。

また、集計対象となる担当者別、顧客別の集計の対象データのイメージを下記の表1に示します。
さらに、この表から担当者Aについて集計した結果の表示イメージを表2に示します。
この集計方法は、〇は実施予定項目としてウント、◎(2重丸)は実績としてカウント、
空白は実施予定及び実績のどちらにもカウントしません。
達成率は両者の比(◎のカウント)/(○のカウント)として計算します。
Excelならば簡単に集計できますが、Kintone上で集計したく、もがいています。
どうかよろしくお願いします。

 

 

やや強引な手法ですが、下記のように書けると思います。

<方法>

  1. forms という変数に、JavaScriptのサンプルコードに該当する内容を設定します。

  2. kintone のヘッダエリアに1.で作成した forms の内容を埋め込みます。

<準備>

kintoneのJavaScriptカスタマイズ設定で jQuery を有効にしてください。
https://developer.cybozu.io/hc/ja/articles/204695384-%E7%AC%AC12%E5%9B%9E-jQuery%E3%82%92%E5%88%A9%E7%94%A8%E3%81%97%E3%81%A6%E3%81%BF%E3%82%88%E3%81%86

<コード>

jQuery.noConflict();
(function($) {
"use strict";
kintone.events.on("app.record.index.show", function(e) {
var forms = (function() {
/*
<form action="#">
<table>
<tr>
<td>
<select name="parentS" onchange="createChildOptions(this.form)" style="width:200px;">
<option value="">親ジャンルを選択して下さい</option>
<option value="1">Fruits</option>
<option value="2">Vegitables</option>
<option value="3">Driknks</option>
</select>
</td>
<td><!--表示位置--><div id="childS"></div></td>
</tr>
</table>
<input type="button" value="選択内容を確認" onclick="chkSelect(this.form);" />
</form>

<script type="text/javascript">
// 子ジャンル(selectC)用の配列
var item = new Array();

item[0] = new Array();
item[0][0]="---------------------";

// Fruits
item[1] = new Array();
item[1][0]="子ジャンルを選択して下さい";
item[1][1]="Apple";
item[1][2]="Banana";
item[1][3]="Melon";
item[1][4]="Grape";

// Vegitables
item[2]= new Array();
item[2][0]="子ジャンルを選択して下さい";
item[2][1]="Potato";
item[2][2]="Carrot";
item[2][3]="Tomato";

// Drinks
item[3] = new Array();
item[3][0]="子ジャンルを選択して下さい";
item[3][1] = "Splite";
item[3][2] = "Beer";
item[3][3] = "Cola";
item[3][4] = "Coffee";
item[3][5] = "Milk Tea";

// 子ジャンルのID名
var idName="childS";

// 親ジャンルが変更されたら、子ジャンルを生成
function createChildOptions(frmObj) {
// 親ジャンルを変数pObjに格納
var pObj=frmObj.elements["parentS"].options;
// 親ジャンルのoption数
var pObjLen=pObj.length;
var htm="<select name='childS' style='width:200px;'>";
for(i=0; i<pObjLen; i++ ) {
// 親ジャンルの選択値を取得
if(pObj[i].selected>0){
var itemLen=item[i].length;
for(j=0; j<itemLen; j++){
htm+="<option value='"+j+"'>"+item[i][j]+"<\/option>";
}
}
}
htm+="<\/select>";
// HTML出力
document.getElementById(idName).innerHTML=htm;
}

// 選択されている値をアラート表示
function chkSelect(frmObj) {
var s="";
var idxP=frmObj.elements['parentS'].selectedIndex;
var idxC=frmObj.elements['childS'].selectedIndex;
if(idxP>0){
s+="親ジャンルの選択肢:"+frmObj.elements['parentS'][idxP].text+"";
if(idxC > 0){
s+="子ジャンルの選択肢:"+frmObj.elements['childS'][idxC].text+"";
}else{
s+="子ジャンルが選択されていません";
}
}else{
s+="親ジャンルが選択されていません";
}
alert(s);
}

// onLoad時にプルダウンを初期化
function init(){
htm ="<select name='childS' style='width:200px;'>";
htm+="<option value=''>"+item[0][0]+"<\/option>";
htm+="<\/select>";
// HTML出力
document.getElementById("childS").innerHTML=htm;
}

// ページ読み込み完了時に、プルダウン初期化を実行
window.onload=init;
</script>
*/
}).toString().match(/\/\*([^]*)\*\//)[1];
$(kintone.app.getHeaderMenuSpaceElement()).append(forms);
return e;
});
})(jQuery);

安藤 光昭様より非常にありがたいreplyを頂きました。
さっそく、返信いただいたとおり、
①参考WebのHTMLを、Kintone カスタマイズビューのhtml設定部分に
丸ごと貼り付けました。
②そして送付いただいJavascriptソース部分をカスタマイズソースとして、
PC用のJavaScript設定部でソースとして設定し、動作させました。
(JQueryは 3.2.0を使用)

その結果、(1)想定していたalert出力については入力値を正しく反映して
出力されました。
しかし、(2)Kintone一覧画面では添付画像のように2か所にプルダウンメニュ
が表示されました。左に表示されたプルダウンは中途半端にしか動作せず、
中央のプルダウンは思惑通りに動作します。どこをどういじれば左のメニュを
非表示にできますでしょうか? 教示お願いします。
(3)また、初回の問い合わせ時にWebのサンプル通りの動作ができることが
目標としましたが、実際には、プルダウンで入力された入力情報を
新しい変数に入力し、これをすでに作成済みの集計及び作表する関数への
引数にして渡そうとしています。どのあたりで取り出せばよろしいでしょうか?
alertの引数s あるいは、formsをConsole.log出力しようとしていますが
上手くいきません。

 (4)また、きわめて基本的な質問ですが、私が何度かトライした
select name=  action= (あるいは onchange) では、②のHTML設定部分に
<script>タグを設けて、onchangeの関数をここで定義しても、
Chromeのデバッガーから関数未定義と判定され動作しませんでした。
このため、Kintoneでは HTML部分にJavaScriptを記述しても無視される
仕様なのかと思っていましたが、教示頂いた方法では、HTML部分に<script>があり、
それが正しく動作しているようです。この辺のところを解説している記事
などありましたら合わせて教示ください。

集計及び作表関数との結合を始めていきます。引き続き世話になるかと
思いますが、よろしくお願いいたします。

 

> しかし、(2)Kintone一覧画面では添付画像のように2か所にプルダウンメニュ
> が表示されました。

これは、私の書いたコードの目的がヘッダ位置にプルダウンを表示するためのものだったからです。
そのため、(1) と (2) で2つ追加されたため結果として2箇所に表示されるようになったと思われます。

> 教示頂いた方法では、HTML部分に<script>があり、
> それが正しく動作しているようです。この辺のところを解説している記事
> などありましたら合わせて教示ください。

私の書いたコードでは、Elementとして <script> タグを含むHTMLをヘッダ部分に挿入しているためHTML内に埋め込まれています。

var forms = (function() {
/*

          (HTML)

*/
}).toString().match(/\/\*([^]*)\*\//)[1];

上記の処理は、formsという変数に (HTML) の部分に記載したHTMLをElementとして格納します。

その後、

$(kintone.app.getHeaderMenuSpaceElement()).append(forms);

としているため、getHeaderMenuSpaceElement() で取得したHTML要素の中に、前述の処理で作成した froms を埋め込んでいます。

*** ですが ***

目的が、Scriptタグの中身を動作させたいだけであれば、jsファイルに<script>タグの中身を書くだけで良いと思います。

jQuery.noConflict();
(function($) {
"use strict";
kintone.events.on("app.record.index.show", function(e) {
 //サンプルソースの
   varitem =newArray();
  //から
   window.onload=init;
  //まで
return e;
});
})(jQuery);

をカスタマイズソースとして、PC用のJavaScript設定部でソースとして設定する方法で良いかもしれません。
(私のコードはおそらく不要です)

安藤様 
お忙しい中、ご教示ありがとうございます。
教えていただいた通り、下記のようにしました。

(A) ご指導いただいたとおりの実行

 

◎実行結果:上記設定の後「アプリの更新」をして、chromeのデバッガーを立ち上げてから
親メニューを選択すると、コンソールに下記エラーが出て、
          createChildOptions is not defined
子メニューは選択できない状態で停止しました。

(B)関数 createChildOptions と 関数 chkSelect とを HTMLに移動
(A)のエラーの原因は form実行時に定義した関数がシステムに読みこまれていないためかと考え、
formの実行に関係する 関数createChildOptionsと、関数 chkSelect の定義を、HTML設定部
に移動して再度実行しましたが、まったく同じく createChildOptions の定義エラーとなりました。
そこで、Javascriptの記述方法のWebページの「関数の定義と呼び出し方法」
http://www.openspc2.org/JavaScript/study/script.html
にあるように、関数の定義を<head>タグ内に移動して実行しました。

(3)変更後のHTML設定部


◎結果はAと同様で下記のエラーでした。
Uncaught ReferenceError: createChildOptions is not defined

<script>タグ内に、関数を定義してあっても、Kintoneで実行すると関数未定義となります。HTML設定部分のどこが間違っているのでしょうか?
この問題をずっと解決できずに悩んでいます。
よろしくお願いします。

カスタマイズレコード一覧のHTMLと、JavaScriptカスタマイズは直接参照ができないようになっています。

今回のパターンで言うと、JavaScriptに書かれている createChildOptions は、カスタマイズレコード一覧のHTML内に存在する下記のタグからは参照ができません。

<select name="parentS" onchange="createChildOptions(this.form)" style="width:200px;">

ではどうするかと言うと、次の2段階の手順になります。

(1) カスタマイズレコード一覧で、JavaScript を呼び出したいタグと form タグに id を設定しイベントハンドラは削除する

<form id="customForm"action="#">

//省略

<select id="parentSelect" name="parentS" style="width:200px;">

//省略

\<input id="resultButton" type="button" value="選択内容を確認" /\>

(2) 設定した id にイベントハンドラを設定し、呼び出す関数を少し変更する

jQuery.noConflict();
(function($) {
// "use strict"; はコメントにする

kintone.events.on("app.record.index.show",function(e) {
        //イベントハンドラの追加
        $(’#parentSelect').on('change', createChildOptions);
        $(’#resultButton').on('click', chkSelect);

 //サンプルソース開始
  varitem =newArray();

//省略

// 親ジャンルが変更されたら、子ジャンルを生成 (関数の最初の2行を変更)
function createChildOptions() {
var frmObj =document.getElementById("customForm");

//省略
}

// 選択されている値をアラート表示 (関数の最初の2行を変更)
function chkSelect() {
var frmObj =document.getElementById("customForm");

//省略
}

//省略
  
 window.onload=init;
 //サンブルソース終了  
  
 returne;
});
})(jQuery);

これで多分動くんじゃないかと思います。

※注意

色々とJavaScriptのコードとしてはイマイチな部分があります。

”とりあえず”動くと思われるコードを書いてます。

安藤様
お忙しい中、日付が変わった 深夜 1:07まで対応いただき誠にありがとうございます。
感謝に 堪えません。
ご指導いただいた通り、HTML設定部分と、カスタムJavascriptソース部分を変更して
実行したところ、意図通りのalert出力を確認できました。
>色々とJavaScriptのコードとしてはイマイチな部分があります
とおっしゃる通り、動作はするもののdebuggerでは

・Uncaught ReferenceError: createChildOptions is not defined
at HTMLSelectElement.onchange
・Uncaught ReferenceError: chkSelect is not defined
at HTMLInputElement.onclick

の二つのエラーが発生していました。
当初の目的が達成できればこのエラーは私は無視してもよいと思っています。
ただ、以前にも触れましたが、alertでの画面へ出力は動作を確認する
ための手段であり、実際にはメニューの選択結果を別途作成してある関数に
引き渡したいと考えています。
これには、callback関数や、Promise関数を使えば良さそうだとは思うのですが、
実際にどうコーディングすればよいのかわからない状態です。
下記に別途作成した表の集計と出力するアプリの構造を示します。

関数 myDisplayCustomizedViewの引数に メニューで取得した集計対象を
加えたいと思いますが、どうすればよろしいでしょうか?
おんぶにだっこのお願いで恐縮ですが、引きつづきご指導お願いします。

alert(s) の代わりに、メニューの値を使いたいという意味ですかね?

function chkSelect() {
var frmObj = document.getElementById("customForm");

//sをオブジェクト型に変更
var s={parent: '', child: ''};
var idxP=frmObj.elements['parentS'].selectedIndex;
var idxC=frmObj.elements['childS'].selectedIndex;
if(idxP>0){
s.parent = frmObj.elements['parentS'][idxP].text;
if(idxC > 0){
s.child = frmObj.elements['childS'][idxC].text;
}
}
myDisplayCustomizedView(records, s);
}

function myDisplayCustomizedView(records, s) {
console.log('親: ' + s.parent);
console.log('子: ' + s.child);
}

みたいに書いたらどうでしょうか?

藤様

重ね重ねの対応ありがとうございます。

(A)ご指導通りの実行結果

ご指導いただいた通り、sを配列にしてparent及びchild の値をこのsに代入し、
関数 myDisplayCustomizedView(records, s) を alertに換えてそのに位置に
挿入して実行してみましたが、今度は、同じく myDisplayCustomizedView
の引数で別関数で読み込んでいる records が未定義エラーとなり途中で実行が
止まってしまいます。それではということで、次のようにしてみました。

(B) UserSelection (名前空間のl変数) を利用した方法

(① parent及びchildの選択値を格納する名前空間の変数 var UserSelection={};を用意し、
この空間の変数として UserSelection.Parent,UserSelection.Child,にプルダウンメニュで
設定された選択値を代入し、myDisplayCustomizedViewの引数として設定します。
②下記順序で関数を呼び出しました。

このケースの結果は、myDisplayCustomizedView内で出させている alertが起動し、
UserSelection.Parent及びUserSelection.Child の値は未定義となってしまいます。
JavaScriptが非同期で実行されているようです。

ここから先は関数の実行順序を制御するために、コールバック関数が必要なのかと考えています。
しかし、コールバック関数の呼び方が等がよく理解できていないのアドバイスをいただけると嬉しいです。
よろしくお願いします。

ボタンを押した時に、2段階選択の内容を使ってデータを再検索する、という仕様でしょうか?

であれば、ボタンクリックのイベントハンドラの中で検索処理を実行する必要があります。

functionchkSelect() {
varfrmObj =document.getElementById("customForm");

//sオブジェクトに値を格納
vars={parent:'',child:''};
varidxP=frmObj.elements['parentS'].selectedIndex;
varidxC=frmObj.elements['childS'].selectedIndex;
if(idxP>0){
s.parent = frmObj.elements['parentS'][idxP].text;
if(idxC >0){
s.child = frmObj.elements['childS'][idxC].text;
}
}
   kintone.api(kintone.api.url('/k/v1/records', true), 'GET', param, function(resp) {
if (resp.records.length > 0) {
myDisplayCustomizedView(resp.records, s);
}
});
}
functionmyDisplayCustomizedView(records, s) {
   console.log(records);
console.log('親: '+ s.parent);
console.log('子: '+ s.child);
}

> コールバック関数の呼び方が等がよく理解できていない

コールバック関数は、元の関数の処理が終わった時に呼び出される、という風に考えるとわかりやすいかもしれません。

 

上記の kintone.api() を例にすると、この関数の構造は kintone.api(P1, P2, P3, P4) となっています。

P1: 呼び出すAPIのURL = kintone.api.url(‘/k/v1/records’, true)

P2: 呼び出しの時のメソッド = ‘GET’

P3: APIに送るパラメータ = param

P4: コールバック関数 = 下記

function(resp) {
if(resp.records.length >0)
myDisplayCustomizedView(resp.records, s);
}
}

処理の流れは、P1〜P3を使ってAPIを実行し、成功したらP4のコールバック関数を呼び出すという風になってます。

 

 

似たようなのに、イベントハンドラというのがあって、これはイベントが発生した時に関数を呼び出すというものです。

kintone.events.on(P1, P2)の場合

P1: イベントの種類を記載 = “app.record.index.show”

P2: イベントハンドラ = 下記

function(e) {  
   // "app.record.index.show"イベントが発生した時に実行する処理  
 console.log(e);  
 return e;  
}  

 

少し前に私が書いた下記の処理は、ボタンにイベントハンドラを追加しています。

$(’#resultButton').on('click', chkSelect);

$(’#resultButton’).on(P1, P2);

P1 = イベントの種類 = ‘click’ イベント

P2 = イベントハンドラ = chkSelect関数

安藤様

土日にもかかわらず、対応いただきまして誠にありがとうございます。
ご指導いただいたように、kintone.events 処理の中に、Kintone apiを取り込み
登録されたデータのレコードと、プルダウンメニューの入力情報を引数として実行しました。

結果は、
①プルダウンの 関数 myDisplayCustomizedView の中に設定した
alert出力においてparent,childの各変数はいずれもが未定義だと
実行直後に出力されました。
②続いて、すでに作成済みのデータ編集して作表する機能が動作し表が出力されました。
ただし、この時の集計対象は、①のプルダウンの設定情報ではなく、
デバッグ用にJavascritソースにあらかじめ埋め込んであった情報でした。
③②の表の出力が終わると、親のプルダウンメニュー(子のブルダウンは非表示)が操作可能になり、
親メニューを選択後、子メニューを選択して、「選択内容の確認」を選ぶと処理が止まってしまうようです。
デバッガーでその後の処理を追いかけようとしましたが、できませんでした。

そこで、ご指導頂いた構成において 関数 myDisplayCustomizedView を呼びだしている
ところで、myDisplayCustomizedViewの関数 を callBack関数にして見ました。
その構成は下記です。

最後の行でプルダウンメニューを実行させ、それが終了したらコールバックを介して、myDisplayCustomizedView  を実行させようさせました。

  この実行結果は、プルダウンメニューの入力は親及び子もできますが、選択確認ボタンを押した後
処理が止まってしまいました。 dubuggerでもその後の動きを追いかけられませんでした。
やはり、コールバック関数の使い方が間違っていると思われます。

よろしくご指導を願いします。

> ご指導いただいたように、kintone.events 処理の中に、Kintone apiを取り込み
> 登録されたデータのレコードと、プルダウンメニューの入力情報を引数として実行しました。

 

いいえ、そうではないです。

 

プルダウンの処理が終わった時 = 選択内容を確認ボタンを押した時 = chkSelect関数の中

 

に検索処理を書いてください。

 

<考え方>

いつ、どのタイミングで、選択内容に合わせた検索するのですか?

=> そのタイミングの時に発生するイベントハンドラに検索処理を書きます。

 

<今の状態>

“app.record.index.show” イベントのイベントハンドラに設定されている

=> 一覧画面が表示された時に検索されている

=> 一覧画面が表示された時は、誰も選択内容を選んでないので、選択リストの内容は定義されない

 

ところで・・・

 

> そこで、ご指導頂いた構成において 関数 myDisplayCustomizedView を呼びだしている
> ところで、myDisplayCustomizedViewの関数 を callBack関数にして見ました。

 

上記の文章は、何のcallBack関数にされているのか、コードを見ても理解できませんでした。

おそらく、callBack関数のご理解が間違っていると思います。

安藤様
大変失礼しました。 ご指摘を受けてよく確認したら、chkSelectのの関数の中ではなく、
外がわで、
kintone.api(kintone.api.url(‘/k/v1/records’, true), ‘GET’, requestParam, function(resp) {

myDisplayCustomizedView(resp.records, USelection);
を呼んでしました。chkSelecの終了位置を読み間違えていました。

訂正して実行させてみたら、プルダウンの入力情報を正しくalertで表示した後、
すでに作成済みのデータ編集して作表する機能が動作し表が出力されました。
ヤッタ!!!

大変ありがとうございます。これでやりたがったことの見通しがつきました。
今度は、メニューでの選択結果を 作表処理と連結させるところまで、実行してみます。
そして再度報告します。
粘り強く、ご親切なご指導に心から深く感謝いたします。取り急ぎ連絡まで。
ありがとうございました。

やりましたね!

 

こちらも解決につながって良かったです (^ ^

安藤様

連絡遅くなりました。全体を通しで実行するのに少し手間取りました。
表の集計処理と出力処理はサンプルデータをもとに作成していましたが、実データは
構造が少し異なっていたため手直しに時間を取られました。
最終的には、当初想定通りの結果を出すことができました。
安藤様のご支援のお陰でプルダウンメニューの大きな壁をなんとか乗り越えることができました。
また、 安藤様のご指導をなぞっていく過程で、javascriptの理解も少しは進みました。
ここに 改めて、厚く御礼申し上げます。

追伸:
現状のプルダウンメニュー方式は組織名、担当者名をhtmlやjavascriptソースコードに
ハードコーディングする必要があります。今後の職制変更などにともなう保守を考えると
ハードコーディングレスの別の方法を今後検討していきたいと思っています。
本件はこれにてクローズとさせていただきます。
ご支援本当にありがとうございました。

 

このトピックはベストアンサーに選ばれた返信から 3 日が経過したので自動的にクローズされました。新たに返信することはできません。