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

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

新規登録して質問してみよう
ただいま回答率
85.31%
Google スプレッドシート

Google スプレッドシートは、フリーで利用できる表計算ソフト。Webアプリのためインターネットに接続することで利用できます。チャートやグラフの作成のほか、シートを他のユーザーと共有したり、同時に作業を進めることも可能です。

Google Apps Script

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

HTML

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

Q&A

解決済

1回答

749閲覧

dopost(e)使用時のリロードによる二重送信対策を教えて下さい。

shoshinsha_001

総合スコア4

Google スプレッドシート

Google スプレッドシートは、フリーで利用できる表計算ソフト。Webアプリのためインターネットに接続することで利用できます。チャートやグラフの作成のほか、シートを他のユーザーと共有したり、同時に作業を進めることも可能です。

Google Apps Script

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

HTML

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

0グッド

0クリップ

投稿2024/01/16 15:37

実現したいこと

HTMLページのレコードから選択したデータから編集フォームに移動し、データ編集後に更新ボタンを押すと対応されるスプレッドシートのデータが更新されるようにしたいです。

①元のデータベース
①元のデータベース(スプレッドシート)


②HTMLレコードページ
②HTMLレコードページ
レコードから編集したいリンクを選択


③編集フォーム
③編集フォーム
編集したいデータを変更

③編集フォーム
編集が完了したら「更新ボタン」を押す


イメージ説明
⑤データベースのデータ更新

前提

gasで営業報告Webアプリを作成しています。
編集フォームからデータ編集後、データ更新をするとスプレッドシートの対象データも更新されるというものなのですが、更新と同時にシートの最終行にも同じデータが書き込まれます。
おそらくリロードによる二重送信が起きてしまっているのではないかと思います。
ただ、色々とコードをいじったものの改善されずお手上げ状態です。
問題点や別の方法などがありましたらご教示いただきたいです。
まだまだ初心者ですので説明がおかしい部分や、コードもおかしな部分が多々あるとは思います。
ご容赦ください。

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

返信フォームよりデータの更新時に、スプレッドシートの対応するデータの更新と別に最終行に同じデータが書き込まれてしまいます。

③編集フォーム
編集後、「更新ボタン」を押す。


イメージ説明
編集したデータが二重送信されて、スプレッドシートの最終行にも書き込みされる。

該当のソースコード

※新規作成フォームは本件と関係ないので省略しています。

form.edit.html

1<!DOCTYPE html> 2<html> 3 <head> 4 <base target="_top"> 5 <script> 6 // ページロード時に報告No.に基づいてデータを取得して入力ボックスに設定する関数 7 function loadFormData() { 8 var reportNo = <?= reportNo ?> 9 10 google.script.run.withSuccessHandler(setFormData).getDataForEdit(reportNo); 11 } 12 13 // 取得したデータを入力ボックスに設定する関数 14 function setFormData(str_data) { // 文字列として受け取る 15 data = JSON.parse(str_data); // 配列に戻す 16 document.getElementById('report_no').value = data[0]; 17 document.getElementById('date').value = data[1]; 18 document.getElementById('company').value = data[2]; 19 document.getElementById('contents').value = data[3]; 20 console.log(data); 21 } 22 23 // ページロード後5秒後にloadFormData関数を呼び出す 24 window.onload = function() { 25 setTimeout(loadFormData, 1000); // 1000ミリ秒 (秒) 後に実行 26 }; 27 </script> 28 <script> 29 function submitEditForm() { 30 document.getElementById("editform").submit(); // フォームを送信 31 } 32 33 // フォームの送信後に実行される関数 34 function onSubmitFormSuccess() { 35 // 送信が成功したら、updateSheet 関数を呼び出す 36 google.script.run.updateSheet(report_no,date,company,contents); 37 } 38 </script> 39 40 </head> 41 <body> 42 <div> 43 <!-- メインコンテンツ --> 44 <main> 45 <div> 46 <form id="editform" method="POST" action="<?= deployURL ?>"> 47 <h2><営業部入力></h2> 48 <div> 49 <table> 50 <tr> 51 <th>報告No</th> 52 <td><input type="text" name="report_no" id="report_no" placeholder="No."></td> 53 54 <th>日付</th> 55 <td><input type="text" name="date" id="date"></td> 56 57 <th>企業名</th> 58 <td><input type="text" name="company" id="company"></td> 59 60 <th>営業内容</th> 61 <td><input type="text" name="contents" id="contents" ></td> 62 </tr> 63 </table> 64 </div> 65 </form> 66 <!-- ボタン --> 67 <input class="submit" type="button" value="更新" onclick="submitEditForm()"> 68 <a href="<?= getAppUrl() ?>?p=index">キャンセル</a> 69 </div> 70 </main> 71 </div> 72 </body> 73</html>

gas

1const doGet = (e) =>{ 2 const page = (e.parameter.p || "index"); 3 4 const template = htmlOutput = HtmlService.createTemplateFromFile(page); 5 template.deployURL = ScriptApp.getService().getUrl(); 6 template.reportNo = e.parameter.reportNo; 7 return template 8 .evaluate() 9 .setTitle('営業報告') 10 .addMetaTag('viewport','whidth=device-width,initial-scale=1'); 11}; 12 13function getAppUrl(){ 14 return ScriptApp.getService().getUrl(); 15} 16 17// 報告データの取得&表示 18function getreportData() { 19 var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('営業報告'); 20 var data = sheet.getRange('A2:K').getValues(); 21 22 // HTML ページの生成 23 var htmlOutput = HtmlService.createHtmlOutput(); 24 25 // テーブルデータ 26 for (var row = 0; row < data.length; row++) { 27 // データが空でない場合のみテーブルに追加 28 if (data[row][0] !== "") { 29 htmlOutput.append('<tr>'); 30 for (var col = 0; col < data[row].length; col++) { 31 var reportNo = data[row][0]; // 受付No.を取得 32 Logger.log(reportNo); 33 htmlOutput.append('<td><a href="' + getAppUrl() + '?p=form.edit&reportNo=' + reportNo + '">' + data[row][col] + '</a></td>'); 34 } 35 htmlOutput.append('</tr>'); 36 } 37 } 38 // HTML ページの表示 39 return htmlOutput.getContent(); 40} 41 42// データ取得用関数 43function getDataForEdit(reportNo) { 44 var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('営業報告'); 45 var data = sheet.getRange('A2:D').getValues(); 46 47 // 報告No.に対応する行を検索 48 for (var row = 0; row < data.length; row++) { 49 // 1列目のデータが reportNoに一致する場合、その行を配列として返す 50 if (data[row][0] === reportNo) { 51 Logger.log(data[row]); 52 return JSON.stringify(data[row]); // 配列を文字列化して返す 53 } 54 } 55 56 // 該当データが見つからなかった場合は空のデータの配列を返す 57 return JSON.stringify(['','','','']); 58} 59 60function doPost(e) { 61 // POSTデータから必要な情報を取得 62 var report_no = e.parameter.report_no; 63 var date = e.parameter.date; 64 var company = e.parameter.company; 65 var contents = e.parameter.contents; 66 67 // 特定のパラメータが空でない場合のみスプレッドシートに書き込み 68 69 //[新規フォーム処理 省略] 70 71 updateSheet(report_no,date,company,contents); 72 73 74 const template = HtmlService.createTemplateFromFile('index'); 75 template.deployURL = ScriptApp.getService().getUrl(); 76 const htmlOutput = template.evaluate() 77 .setTitle('営業報告') 78 .addMetaTag('viewport', 'width=device-width,initial-scale=1'); 79 return htmlOutput; 80} 81 82 //[新規フォーム処理 省略] 83 84// スプレッドシートに書き込み 85function updateSheet(report_no,date,company,contents) { 86 var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('営業報告'); 87 var data = sheet.getRange('A2:D').getValues(); 88 89 // 受付No.に対応する行を検索 90 for (var row = 0; row < data.length; row++) { 91 // データが空でない場合のみ更新 92 if (data[row][0] === report_no) { 93 data[row] = [report_no,date,company,contents]; 94 sheet.getRange(row + 2, 1, 1, data[row].length).setValues([data[row]]); 95 break; // 更新が完了したらループを抜ける 96 } 97 } 98}

試したこと

二重送信回避のためにgoogle.script.runでも試してみました。
二重送信はされなくなりましたが、フォーム送信後にindex.htmlにページ遷移されなくなりました。

form.edit.html

1<!DOCTYPE html> 2<html> 3 <head> 4 <base target="_top"> 5 <script> 6 // ページロード時に報告No.に基づいてデータを取得して入力ボックスに設定する関数 7 //[文字数制限により処理省略] 8 </script> 9 <script> 10 function submitEditForm() { 11 report_no = document.getElementById('report_no').value; 12 date = document.getElementById('date').value; 13 company = document.getElementById('company').value; 14 contents = document.getElementById('contents').value; 15 16 google.script.run.withSuccessHandler(updateSuccess).updateSheet(report_no,date,company,contents); 17 } 18 function updateSuccess(redirectUrl) { 19 alert("データが正常に更新されました。"); 20 window.location.href = redirectUrl; 21 } 22 </script>

gas

1// スプレッドシートに書き込み 2function updateSheet(report_no,date,company,contents) { 3 var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('営業報告'); 4 var data = sheet.getRange('A2:D').getValues(); 5 6 // 受付No.に対応する行を検索 7 for (var row = 0; row < data.length; row++) { 8 // データが空でない場合のみ更新 9 if (data[row][0] === report_no) { 10 data[row] = [report_no,date,company,contents]; 11 sheet.getRange(row + 2, 1, 1, data[row].length).setValues([data[row]]); 12 break; // 更新が完了したらループを抜ける 13 } 14 } 15var redirectUrl = ScriptApp.getService().getUrl() + '?p=index'; 16 return redirectUrl; 17}

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

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

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

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

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

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

guest

回答1

0

ベストアンサー

※新規作成フォームは本件と関係ないので省略しています。

本当にそうでしょうか?

  • A(修正前のコード):submitEditForm()関数内の submit() でフォーム内容をPOSTしたら、「二重送信」になった。(厳密にいえば"修正対象行だけでなく、同じ内容のデータが末尾行にも追加された")。

  • B(修正後のコード):google.script.run.witghSuccessHandler を使って、ボタン押下後直接、updateSheetを呼び出したら、「二重送信」ではなくなった。

AはdoPostを呼び出します。
BはdoPostを呼び出しません。
この事実、「新規作成」という言葉、そして「末尾に追加されてしまう」という状況から推測すると、doPost の中にある 「 //[新規フォーム処理 省略]」が悪さしてるとしか考えられません。
(ここまでたどり着くのにコード読みながら 5秒くらいかかりました)

まあ、本当のところは省略されてるのでわかりませんが。

多分ですが「HTMLから渡された report_no を スプレッドシートのA列のデータと比較して、どの行の A列の値とも異なっていれば末尾行に追加」みたいなコードを書かれたんでしょうけれど、その中にバグがあって、本来末尾に追記してはいけないのに追記してしまうようなアルゴリズムになってると思われます。
(つまり二重送信でもなんでもなくて、単なる制御ミス)
(そしてそのコードはupdateSheetをコピペしてちょっと変えたような感じになってると推測)

updateSheetをコピペしてちょっと変えたコードでバグを産んじゃうくらいなら、ここは、updateSheet 関数の中で更新と追加両方の機能を持たせちゃえばいいんではないでしょうか。

gas

1 2前略 3 4 5function doPost(e) { 6 // POSTデータから必要な情報を取得 7 var report_no = e.parameter.report_no; 8 var date = e.parameter.date; 9 var company = e.parameter.company; 10 var contents = e.parameter.contents; 11 12 // 特定のパラメータが空でない場合のみスプレッドシートに書き込み 13 14 //[新規フォーム処理 省略] ←←不要なので削除 15 16 updateSheet(report_no,date,company,contents); 17 18 const template = HtmlService.createTemplateFromFile('index'); 19 template.deployURL = ScriptApp.getService().getUrl(); 20 const htmlOutput = template.evaluate() 21 .setTitle('営業報告') 22 .addMetaTag('viewport', 'width=device-width,initial-scale=1'); 23 return htmlOutput; 24} 25 26 //[新規フォーム処理 省略] ←不要なので削除 27 28// スプレッドシートに書き込み 29function updateSheet(report_no,date,company,contents) { 30 var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('営業報告'); 31 var data = sheet.getRange('A2:D').getValues(); 32 33 // 受付No.に対応する行を検索 34// スプレッドシートに書き込み 35function updateSheet(report_no,date,company,contents) { 36 var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('営業報告'); 37 var data = sheet.getRange('A2:D').getValues(); 38 39 // 受付No.に対応する行を検索 40 for (var row = 0; row < data.length; row++) { 41 // A列が同じreport_noならば更新 または 空白ならば追加 42 if (data[row][0] === report_no || data[row][0] === "") { 43 sheet.getRange(row + 2, 1, 1, data[row].length).setValues([[report_no,date,company,contents]]); 44 break; // 目的を達したのでループから抜ける。 45 } 46 // 同じreport_noが見つかるか、空白になるまでループ。 47 } 48}

(※ほかのコードは、すべて前半=修正前のまま)

投稿2024/01/17 13:14

編集2024/01/17 13:24
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

shoshinsha_001

2024/01/17 15:22

ご回答ありがとうございます。 新規フォームは空なので悪さしていないと思っていました💦 空のデータの末尾に修正フォームのデータがくっついてしまっている場合もあるんですね いろんな所から引っ張ってきたコードをしたい処理に合わせて無理やり改編させてしまっていたので、どこかでバグが生まれていたのかもしれません 他の方法のご教授やコードまでありがとうございます。 もっと、基本てな部分を勉強していきたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.31%

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

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

質問する

関連した質問