質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

Q&A

解決済

2回答

12695閲覧

[GAS][JavaScript]GASとJavaScript間でデータをやり取りする(関数、引数、戻り値)

Arisa.Sunagawa

総合スコア15

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

0グッド

0クリップ

投稿2018/12/16 15:11

編集2018/12/17 12:13

前提・実現したいこと

Webページ内のボタンを押下した際にJavaScriptの関数からGASの関数を呼び出し、
JavaScriptからGASへ、またGASからJavaScriptへ関数の引数をわたし、戻り値を受け取りたい。

表示するWebページはGoogle Apps Scriptダッシュボード内にあるIndex.htmlとし、
またサーバ側の処理は同ダッシュボード内のコード.gsに記入する。

発生している問題・エラーメッセージ

JavaScriptで受け取った戻り値が全てUndefinedとなり、値をうまく受け取れない。

該当のソースコード

javascript

1/////////////////////////////////////////////////////////////////////////////// 2///// 定数の宣言 ///// 3/////////////////////////////////////////////////////////////////////////////// 4// スプレッドシートのID(https://docs.google.com/d/[ID]/edit) 5var DATABASE_FILE_NAME 6 = '[ID]'; 7 8var DATABASE_SS_MAIN = 'DataBase'; 9var DATABASE_SS_USER_ID = 'UserID'; 10 11// GM登録時に読み込んだセルをDataBaseに反映させる順番 12var DATABASE_CELL = 13 [ 14 'A2', // 日付 15 'B2', // 開始時刻 16 'C2', // 終了時刻 17 'D2', // システム名 18 'E2', // シナリオ名 19 'F2', // 使用ツール 20 'J2', // 募集人数 21 'G2', // 概要 22 ]; 23 24// DataBase 状態 列名 25var DATABASE_STATE_CELL = 'I2'; 26var DATABASE_SENARIO_NAME_COL = 4; 27 28// DataBase 状態 "募集中" 29var ADD_STATE = "募集中"; 30 31// DataBase GM 列名 32var DATABASE_GM_NAME_CELL = 'H2'; 33var DATABASE_MAIL_COL = 0; 34var DATABASE_USERNAME_COL = 1; 35 36/////////////////////////////////////////////////////////////////////////////// 37///// 実際の処理 ///// 38/////////////////////////////////////////////////////////////////////////////// 39/// 40///* サーバ・クライアント接続 */// 41/// 42function doGet() { 43 return HtmlService.createHtmlOutputFromFile("index.html") 44 .setSandboxMode(HtmlService.SandboxMode.IFRAME); 45} 46 47// 48///* Googleアカウントが登録されているかの検査 */// 49// 50function AccountCheck() 51{ 52 // ActiveSheetの取得 53 var databaseFileSheet 54 = SpreadsheetApp.openById(DATABASE_FILE_NAME).getSheetByName(DATABASE_SS_USER_ID); 55 56 // アカウントIDの検査 57 var ret = findRow(databaseFileSheet, 58 Session.getActiveUser().getUserLoginId(), 59 DATABASE_MAIL_COL); 60 61 if(ret == 0) 62 { 63 // Browser.msgBox("ユーザー登録がされていません"); 64 return false; 65 } 66 else 67 { 68 return true; 69 } 70} 71 72function AddSession(cellVal){ 73 // 現在編集中のシートを取得 74 var activeFileSheet 75 = SpreadsheetApp.getActiveSpreadsheet(); 76 77 // データベースファイル 78 var databaseFileSheet 79 = SpreadsheetApp.openById(DATABASE_FILE_NAME).getSheetByName(DATABASE_SS_MAIN); 80 81 // 書き込み行のInsert 82 databaseFileSheet.insertRowBefore(2); 83 84 // 読み込んだ値の書き込み 85 for (i = 0; i < DATABASE_CELL.length; i++) 86 { 87 WriteCell(databaseFileSheet, 88 DATABASE_CELL[i], 89 cellVal[i]); 90 } 91 92 // 募集状況の追加 93 WriteCell(databaseFileSheet, 94 DATABASE_STATE_CELL, 95 ADD_STATE); 96 97 // GM名(GoogleID)の追加 98 WriteCell(databaseFileSheet, 99 DATABASE_GM_NAME_CELL, 100 Session.getActiveUser().getUserLoginId()); 101} 102 103// 104///* シナリオ名かぶり検査 */// 105// 106function SenarioNameCheck(addSenarioNameVal) 107{ 108 var databaseFileSheet 109 = SpreadsheetApp.openById(DATABASE_FILE_NAME).getSheetByName(DATABASE_SS_MAIN); 110 var ret = findRow( 111 databaseFileSheet, 112 addSenarioNameVal, 113 DATABASE_SENARIO_NAME_COL); 114 if(ret != 0) 115 { 116 //Browser.msgBox("シナリオ名が被っています、別の名前を入力してください。"); 117 return false; 118 } 119 return true; 120} 121 122// 123///* 指定セルの値の取得 */// 124// 125function ReadCell(sheet, range) { 126 // 指定セル範囲取得 127 var cellrange = sheet.getRange(range); 128 129 // 値返却 130 return cellrange.getValue(); 131} 132 133// 134///* 指定セルの値の変更 */// 135// 136function WriteCell(sheet, range, setVal) { 137 // 指定セル範囲取得 138 var cellrange = sheet.getRange(range); 139 140 // 値の変更 141 cellrange.setValue(setVal); 142} 143 144// 145///* 行検索処理 */// 146// 147function findRow(sheet,val,col){ 148 //受け取ったシートのデータを二次元配列に取得 149 var dat = sheet.getDataRange().getValues(); 150 151 //値の探索、値が見つかった場合行数を返却 152 for(var i=1;i<dat.length;i++){ 153 //Browser.msgBox(dat[i][col]); 154 if(dat[i][col] === val){ 155 return i+1; 156 } 157 } 158 159 // 値がない場合0を戻す。 160 return 0; 161}

HTML

1<!doctype html> 2<html lang="ja"> 3 <head> 4 <meta charset="utf-8"> 5 <title>Sandbox Agenda</title> 6 </head> 7 8 <script type="text/javascript"> 9 10 // ボタン押下時の処理 11 function btnAddSession_Click() { 12 13 // アカウントチェック 14 var ret = google.script.run.AccountCheck(); 15 // 値がundefinedで比較できていない。 16 if(ret == false) 17 { 18 document.getElementById("txtDebug").value = "登録されてないよ"; 19 return; 20 } 21 else 22 { 23 document.getElementById("txtDebug").value = "登録されてるよ"; 24 } 25 26 /// 入力コントロール名の配列定義 27 /// これHTMLと同じ値を書いてるけどまとめられない?冗長。 28 /// C言語の#defineのような… 29 var TXT_ID = 30 [ 31 "txtStartDate", //開始日時 32 "txtStartTime", //開始時刻 33 "txtEndTime", //終了時刻 34 "txtUseRuleSystem",//システム 35 "txtSenarioName", //シナリオ名 36 "txtUseTool", //使用ツール 37 "txtRecruitNum", //募集人数 38 "txtOverview" //概要 39 ]; 40 41 // 送信用配列の宣言 42 var arrSendData = Array(TXT_ID.length); 43 44 // コントロールの値を取得 45 var i = 0; 46 for(i = 0; i < TXT_ID.length;i++) 47 { 48 // ここセンスない。Valueがnullならと書き換えたい。 49 if(document.getElementById(TXT_ID[i]).value == "") 50 { 51 document.getElementById("txtDebug").value 52 = "値を入力してください"; 53 return; 54 } 55 } 56 57 // シナリオかぶり検査 58 ret = google.script.run.SenarioNameCheck(document.getElementById("txtSenarioName").value); 59 if(ret == false) 60 { 61 // かぶっているのに被っていないと表示される。識別できていない。 62 // 値がundefinedで比較できていない。 63 document.getElementById("txtDebug").value = "シナリオかぶり"; 64 return; 65 } 66 else 67 { 68 document.getElementById("txtDebug").value = "かぶってないよ"; 69 } 70 71 // GAS へデータを送信 72 // 送信データがすべてundefinedになって出力される。なぜ? 73 google.script.run.AddSession(arrSendData); 74 } 75 </script> 76 77 <body> 78 <form> 79 <p> 80 開始日時 81 <input type="text" value="" id="txtStartDate"><br> 82 開始時刻 83 <input type="text" value="" id="txtStartTime"><br> 84 終了時刻 85 <input type="text" value="" id="txtEndTime"><br> 86 システム 87 <input type="text" value="" id="txtUseRuleSystem"><br> 88 シナリオ名 89 <input type="text" value="" id="txtSenarioName"><br> 90 使用ツール 91 <input type="text" value="" id="txtUseTool"><br> 92 募集人数 93 <input type="text" value="" id="txtRecruitNum"><br> 94 概要 95 <input type="text" value="" id="txtOverview"><br> 96 <!-- 登録ボタン --> 97 <input type="button" value="登録" onclick="btnAddSession_Click();" id="btnAddSession"><br> 98 </p> 99 <p> 100 デバッグプリント 101 <input type="text" value="" id="txtDebug"><br> 102 </p> 103 </form> 104 </body> 105</html>

追加の質問

kisojinさんの回答を元にコードを修正しました。
無事GASからJavaScriptへ戻り値を返すことができました。

しかし

  1. GASからの戻り値がfalseだったらreturnするという処理を記述していたが、returnで処理を終了することができなくなった。

  (現在はGlobal変数gs_ExitFlagでフラグを格納して、対応している)
2.JavaScriptからGASへ引数を渡すことができない。
(シナリオ名かぶり検査SenarioNameCheckと
スプレッドシートに配列の値を順番に書き込むAddSessionが機能していない。
AddSessionで書き込まれた値がUndefinedになっている。)

javascript

1 <script type="text/javascript"> 2 /// 入力コントロール名の配列定義 3 var TXT_ID = 4 [ 5 "txtStartDate", //開始日時 6 "txtStartTime", //開始時刻 7 "txtEndTime", //終了時刻 8 "txtUseRuleSystem",//システム 9 "txtSenarioName", //シナリオ名 10 "txtUseTool", //使用ツール 11 "txtRecruitNum", //募集人数 12 "txtOverview" //概要 13 ]; 14 15 // エラーコード 16 var ERR_CODE = 17 { 18 CONTROL_EMPTY : 1, 19 NO_GOOGLE_ID : 2, 20 NOT_UNIQUE_SESSION : 3, 21 }; 22 23 // エラーメッセージ 24 var ERROR_MESSAGE = 25 [ 26 "値を入力してください", 27 "登録されてないよ", 28 "シナリオかぶり" 29 ]; 30 31 // グローバル変数 32 var gs_ExitFlag = 0; 33 34 35 // GAS呼び出し後の処理 36 // アカウントチェック呼び出し 37 function CallGas_AccountCheck(ret) 38 { 39 if(ret == false) 40 { 41 OutputErrorMessage(ERR_CODE.NO_GOOGLE_ID); 42 } 43 } 44 45 // シナリオ名かぶり検査 46 function CallGas_SenarioNameCheck(ret) 47 { 48 if(ret == false) 49 { 50 OutputErrorMessage(ERR_CODE.NOT_UNIQUE_SESSION); 51 } 52 } 53 54 // JavaScript内サブルーチン 55 // HTMLのコントロールの値を配列に格納 56 function GetAllControlValue(TXT_ID) 57 { 58 var i = 0; 59 for(i = 0; i < TXT_ID.length;i++) 60 { 61 // ここセンスない。Valueがnullならと書き換えたい。 62 if(document.getElementById(TXT_ID[i]).value == "") 63 { 64 OutputErrorMessage(ERR_CODE.CONTROL_EMPTY); 65 } 66 } 67 } 68 // 終了フラグをOnにして、エラーメッセージを出力 69 function OutputErrorMessage(MessageID) 70 { 71 if(gs_ExitFlag == 0) 72 { 73 document.getElementById("txtDebug").value 74 = ERROR_MESSAGE[MessageID]; 75 gs_ExitFlag = 1; 76 } 77 } 78 79 80 // ボタン押下時の処理 81 function btnAddSession_Click() 82 { 83 // アカウントチェック 84 google.script.run 85 .withSuccessHandler(CallGas_AccountCheck) 86 .AccountCheck(); 87 88 // コントロールの値を取得 89 var arrSendData = Array(TXT_ID.length); 90 GetAllControlValue(TXT_ID); 91 92 // シナリオかぶり検査 93 google.script.run 94 .withSuccessHandler(CallGas_SenarioNameCheck) 95 .SenarioNameCheck(document.getElementById("txtSenarioName").value); 96 97 // GAS へデータを送信 98 // 送信データがすべてundefinedになって出力される。なぜ? 99 // 変数の型の問題?? 100 if(gs_ExitFlag == 0 ) 101 { 102 google.script.run.withSuccessHandler().AddSession(arrSendData); 103 } 104 } 105 </script>

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

papinianus

2018/12/17 00:59 編集

コードブロックの言語名のところ、[google app script]は認識してくれないので、[javascript]にしてください。 あとスプレッドシートidは共有設定によっては見えてしまうので、伏せ字などにしたほうがいいかと
Arisa.Sunagawa

2018/12/17 12:15

指摘ありがとうございます。修正しました。
guest

回答2

0

ベストアンサー

google.script.runは値を返しません。さらに非同期で動作するため、google.script.run.AccountCheck()が実行している間に次のラインを実行していると思われます。そのため、Google Apps Script側から値を受け取り、その値を使用するためにwithSuccessHandler()を使います。

javascript

1google.script.run.withSuccessHandler(function(ret) { 2 3 // do something 4 5}).AccountCheck();

あるいは、下記のように使用することも可能です。

javascript

1google.script.run.withSuccessHandler(sample).AccountCheck(); 2 3function sample(ret) { 4 5 // do something 6 7}

参考

投稿2018/12/16 22:20

kisojin

総合スコア899

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Arisa.Sunagawa

2018/12/17 12:14

回答ありがとうございます。戻り値を受け取ることができました。 しかし引数を渡すことができずまだ苦しんでいます。 よろしくおねがいします。
Arisa.Sunagawa

2018/12/17 13:20

大変申し訳ありません。 いくつか初歩的なコード間違い(ifの中が===だったり、TrueとFalseを間違えていたり、引数の配列を間違えていたり)の為でした。無事動作しました
kisojin

2018/12/22 07:56

返事が遅くなり申し訳ありません。無事解決できたとのことで安心致しました。ご連絡有難うございます。
guest

0

追加質問の話です

  • arrSendDataがundefined

これはもともとGetAllControlValueが何もしてないからです。
返り値を作って返さないと(もしくはグローバルなarrSendDataにpushしていってもいいですが)、arrSendDataが自動で埋まってくれたりはしません。

javascript

1 // JavaScript内サブルーチン 2 // HTMLのコントロールの値を配列に格納 3 function GetAllControlValue(TXT_ID) 4 { 5 var i = 0; 6 var ret = []; 7 for(i = 0; i < TXT_ID.length;i++) 8 { 9 // ここセンスない。Valueがnullならと書き換えたい。 10 // if(document.getElementById(TXT_ID[i]).value == "") 11 if(document.getElementById(TXT_ID[i]).value) //またはif(document.getElementById(TXT_ID[i]).value === "") 12 { 13 OutputErrorMessage(ERR_CODE.CONTROL_EMPTY); // このエラーの出し方では何の項目がエラーか分かりません。またbreakするとか、エラーだったら送信できないとか手当が必要と思います 14 } 15 ret.push(document.getElementById(TXT_ID[i]).value); //これをelseとかに入れると項目の数があわなくなるので、EmptyStringでも格納する必要がある 16 } 17 } 18 19// こう使う 20 //var arrSendData = Array(Txt_ID.length); //意味がないのでやめましょう。Cなどの配列と違ってもともと可変なのでサイズを指定して確保する必要性がないです 21 var arrSendData = GetAllControlValue(TXT_ID);
  • returnできない

「できない」というのがネガティブな捉えかただとすれば、用途が違うので認識を改めていただく必要があります。
新設したグローバルなフラグが何につかわれるか分かりませんが、非同期というのをもう一度冷静に見直してください。
google.runで非同期にコールされた関数はいつ戻ってくるか保証がありません。その間btnAddSession_Clickはどんどん、次の行、次の行と処理を進めていきます。したがってフラグが埋まるころには、フラグを利用して止まるはずの処理が走り終わっています(google.runは非常に遅いため)。
そうではなくてCallGas_AccountCheckのelse(retがfalseでないとき)で、次のgoogle.runをなげてシナリオチェックをし、そのコールバックであるCallGas_SenarioNameCheckのelse(retがfalseでないとき)に、AddSessionを実行する、というように、非同期のコールバックが次の非同期のコールバックを呼ぶように関数を連鎖させるように組んでいく必要があります(カードゲームで例えると、呪文をスタックに積んで順次解決する感じですかね)。
(補足)コールバックの連鎖って書きましたが、たぶんUXはすごく悪いので(google.runは遅い)、必要な引数は全部まとめてgasになげて、サーバからの戻りは1回のほうが、ユーザの待ちは短くてすむと思う(返り値を{IsSuccess:true/false, ErrorMessages:"Error.Code.Something", Result:""}みたいな形式で返す。

投稿2018/12/17 12:54

編集2018/12/17 13:16
papinianus

総合スコア12705

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Arisa.Sunagawa

2018/12/17 16:34

>arrSendDataがundefined おっしゃる通りです…質問したあとしばらくして気づいてとても恥ずかしくなりました。 >returnできない 非同期のコールバックが次の非同期のコールバックを呼ぶように…ネストが酷いことになりそうですね…Web系の言語は初めてで、組み込み系との違いに戸惑うばかりです…。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問