【问题标题】:Submit form(ReactJS) to Google Spreadsheet - change the message validation将表单(ReactJS)提交到 Google 电子表格 - 更改消息验证
【发布时间】:2020-06-15 08:04:30
【问题描述】:

我想通过 ReactJS 表单在 Google 表格中注册数据(如果用户有可能的建议或 cmets,则为 2 个字段)。

这是我在 React 中的反馈表:

import React,{useState,useEffect} from 'react';
import './App.css';

const formUrl = 'https://script.google.com/macros/s/AK.../exec'
export default function FrmTable(){  
    const [loading,setLoading] = useState(false)
    return(
        <div className="section-form">
            <form name="frm"  
                  method="post"
                  action={formUrl}
            >   
                <div className="form-elements">
                    <div className="pure-group">
                        <label className="pure-group-label">Suggestion content pdf</label>
                        <input id="Suggestion content pdf" name="Suggestion content pdf" className="pure-group-text"
                               type="text" 
                        />
                    </div>


                    <div className="pure-group">
                        <label className="pure-group-label" >Comments</label>
                        <textarea id="Comments" name="Comments" rows="10" className="pure-group-text" 
                          placeholder="" 
                          maxLength="1000"
                        ></textarea>
                    </div>
                </div>

                <p className="loading-txt">{loading == true ? 'Loading.....' : ''}</p>
                <div className="pure-group pure-group-btn">
                    <button className="button-success pure-button button-xlarge btn-style" >Send</button>
                </div>
            </form>
            </div>
    )
}

用于注册建议内容和 cmets 的 GSheet 脚本:

        var SHEET_NAME = "Feedback";
//  2. Run > setup
//  3. Publish > Deploy as web app
//    - enter Project Version name and click 'Save New Version'
//    - set security level and enable service (most likely execute as 'me' and access 'anyone, even anonymously)
//  4. Copy the 'Current web app URL' and post this in your form/script action
//
//  5. Insert column names on your destination sheet matching the parameter names of the data you are passing in (exactly matching case)
var SCRIPT_PROP = PropertiesService.getScriptProperties(); // new property service
// If you don't want to expose either GET or POST methods you can comment out the appropriate function
function doGet(e){
  return handleResponse(e);
}
function doPost(e){
  return handleResponse(e);
}
function handleResponse(e) {
  // shortly after my original solution Google announced the LockService[1]
  // this prevents concurrent access overwritting data
  // we want a public lock, one that locks for all invocations
  var lock = LockService.getPublicLock();
  lock.waitLock(30000);  // wait 30 seconds before conceding defeat.
  try {
    // next set where we write the data - you could write to multiple/alternate destinations
    var doc = SpreadsheetApp.openById(SCRIPT_PROP.getProperty("key"));
    var sheet = doc.getSheetByName(SHEET_NAME);
    // we'll assume header is in row 1 but you can override with header_row in GET/POST data
    var headRow = e.parameter.header_row || 1;
    var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
    var nextRow = sheet.getLastRow()+1; // get next row
    var row = [];
    // loop through the header columns
    for (i in headers){
      if (headers[i] == "Timestamp"){ // special case if you include a 'Timestamp(Date)' column
        row.push(new Date());
      } else { // else use header name to get data
        row.push(e.parameter[headers[i]]);
      }
    }
    // more efficient to set values as [][] array than individually
    sheet.getRange(nextRow, 1, 1, row.length).setValues([row]);
    // return json success results
    return ContentService
          .createTextOutput(JSON.stringify({"result":"success", "row": nextRow}))
          .setMimeType(ContentService.MimeType.JSON);
  } catch(e){
    // if error return this
    return ContentService
          .createTextOutput(JSON.stringify({"result":"error", "error": e}))
          .setMimeType(ContentService.MimeType.JSON);
  } finally { //release lock
    lock.releaseLock();
  }
}
function setup() {
    var doc = SpreadsheetApp.getActiveSpreadsheet();
    SCRIPT_PROP.setProperty("key", doc.getId());
}

一切正常我可以在 GSheet 中注册 2 个字段(建议和评论),但我想在提交 后有另一个视图

我学习了一些教程,因为我是 React 新手。最后提交后,您将被发送到 script.googleusercontent.... 因为在 GSheet 脚本中我们有此代码

return ContentService
              .createTextOutput(JSON.stringify({"result":"success", "row": nextRow}))
              .setMimeType(ContentService.MimeType.JSON);

我只想显示一个简单的消息,例如弹出窗口,以说明提交表单正常。欢迎任何想法:) 非常感谢。

新编辑:我已经更改了我的代码(React + Google 脚本),但我有一个 CORB 阻止跨域。

import React,{useState,useEffect} from 'react';
import './App.css';

const formUrl = 'https://script.google.com/macros/s/AKfycbz4hMELOHff2Yd_ozpOid2cAWFSWPm_7AOD15OIeQRdYrocv0wa/exec'
export default function FrmTable(){  
   
   
    const jsonp = (url, callback) =>  {
        var callbackName = 'jsonp_callback_' + Math.round(100000 * Math.random());
        window[callbackName] = function(data) {
            alert("Formulaire envoyé ");
            delete window[callbackName];
            document.body.removeChild(script);
            callback(data);
        };
    
        var script = document.createElement('script');
        script.src = url + (url.indexOf('?') >= 0 ? '&' : '?') + 'callback=' + callbackName;
        document.body.appendChild(script);
    }

    const mySubmitHandler = (event) => {
        event.preventDefault();
        
        jsonp(formUrl + '?La+FAQ+en+question=' + encodeURIComponent(faqName), (data) => {
          // alert(data);
        });
        event.target.reset();
        
      }
    // const { register, errors, required ,handleSubmit } = useForm();
    const [loading,setLoading] = useState(false)
    const [faqName,setFaqName] = useState('')
  
    const myChangeHandler1 = (event) => {
        setFaqName(event.target.value);
      }
     
    return(
        <div className="section-form" >
            <form name="frm"  
                  method="post"
                  onSubmit={mySubmitHandler}
            >   
                <div className="form-elements">

                    <div className="pure-group ">
                        <label className="pure-group-label">La FAQ en question </label>
                        <input name="FAQ en question" className="pure-group-text"
                               type="text" onChange={myChangeHandler1}
                        />
                    </div>
                </div>

                <input type='submit' />
            </form>
            </div>
    )
}

谷歌脚本:

var SCRIPT_PROP = PropertiesService.getScriptProperties(); // new property service
// If you don't want to expose either GET or POST methods you can comment out the appropriate function
function doGet(e){
  return handleResponse(e);
}
function doPost(e){
  //return handleResponse(e);
}
function handleResponse(e) {
  // shortly after my original solution Google announced the LockService[1]
  // this prevents concurrent access overwritting data
  // [1] http://googleappsdeveloper.blogspot.co.uk/2011/10/concurrency-and-google-apps-script.html
  // we want a public lock, one that locks for all invocations
  var lock = LockService.getPublicLock();
  lock.waitLock(30000);  // wait 30 seconds before conceding defeat.
  try {
    // next set where we write the data - you could write to multiple/alternate destinations
    var doc = SpreadsheetApp.openById(SCRIPT_PROP.getProperty("key"));
    var sheet = doc.getSheetByName(SHEET_NAME);
    // we'll assume header is in row 1 but you can override with header_row in GET/POST data
    var headRow = e.parameter.header_row || 1;
    var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
    var nextRow = sheet.getLastRow()+1; // get next row
    var row = [];
    // loop through the header columns
    for (i in headers){
      if (headers[i] == "Timestamp"){ // special case if you include a 'Timestamp(Date)' column
        row.push(new Date());
      } else { // else use header name to get data
        row.push(e.parameter[headers[i]]);
      }
    }
    // more efficient to set values as [][] array than individually
    sheet.getRange(nextRow, 1, 1, row.length).setValues([row]);
     var callback = e.parameter.callback;
    // return json success results
   // return ContentService
   //       .createTextOutput(JSON.stringify({"result":"success", "row": nextRow}))
   //       .setMimeType(ContentService.MimeType.JSON);
     return ContentService.createTextOutput(callback+'('+ JSON.stringify({"result":"success", "row": nextRow})+')').setMimeType(ContentService.MimeType.JAVASCRIPT);
  } catch(error){
    // if error return this
    //return ContentService
      //    .createTextOutput(JSON.stringify({"result":"error", "error": e}))
//          .setMimeType(ContentService.MimeType.JSON);
    var callback = e.parameter.callback;
     return ContentService.createTextOutput(callback+'('+ JSON.stringify({"result":"error", "error": error})+')').setMimeType(ContentService.MimeType.JAVASCRIPT);
  } finally { //release lock
    lock.releaseLock();
  }
}
function setup() {
    var doc = SpreadsheetApp.getActiveSpreadsheet();
    SCRIPT_PROP.setProperty("key", doc.getId());
}

【问题讨论】:

  • 我认为当HTML的表单提交用于发送数据时,如果您想在从Web Apps(服务器端)检索响应值后在弹出窗口中显示消息,很遗憾,这可能无法直接实现。因为HTML的表单提交不能从服务器端取回返回值,而页面发生变化。那么在这种情况下,如何使用fetch 提交数据呢?通过这种方式,可以检索返回值,并且您可以通过返回值运行操作。请认为这只是几个可能的建议之一。
  • 非常感谢您的回答。是的,在 React 表单末尾使用“发送”按钮发送信息后,我不想将我重定向到图片中的 script.googleusercontent,但在同一屏幕上显示一条消息以表明一切正常。
  • 在本教程中,他们将 JQuery 与 HTML 表单一起使用,但在 React 中禁止使用它:medium.com/@dmccoy/…
  • 感谢您的回复。如果您使用脚本向 Web 应用程序请求,在您的情况下,我认为可以使用 fetchRef这个怎么样?
  • 感谢您回复和更新您的问题。在您更新的问题中,lock.releaseLock() 运行后,没有返回任何值。我认为这可能是您的问题的原因。这个怎么样?

标签: reactjs google-apps-script google-sheets google-sheets-api


【解决方案1】:

我想提出以下两种模式。

修改点:

  • 在当前阶段,没有getPublicLock()
  • 在您的共享电子表格中,只有一张Sheet2。但是在var SHEET_NAME = "Feedback";,没有使用工作表名称。这样,var sheet = doc.getSheetByName(SHEET_NAME); 就是 nullvar headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0] 会出现错误。
  • formUrl + "&amp;Commentaires=" + encodeURIComponent(faqComment)在react端,在这种情况下,端点变成https://script.google.com/macros/s/###/exec&amp;Commentaires=test?callback=jsonp_callback_###

模式一:

在此模式中,您的脚本被修改并使用 JSONP。

反应方:App.js

从:
jsonp(formUrl + "&Commentaires=" + encodeURIComponent(faqComment), data => {
  // alert(data);
});
到:
jsonp(formUrl + "?Commentaires=" + encodeURIComponent(faqComment), data => {
  // alert(data);
});

Google Apps 脚本方面:

当您想使用您的共享电子表格时,请进行如下修改。

从:
var lock = LockService.getPublicLock();
到:
var lock = LockService.getDocumentLock();

还有,

从:
var SHEET_NAME = "Feedback";
到:
var SHEET_NAME = "Sheet2";

在这种情况下,您也可以将工作表名称从Sheet2 修改为Feedback,而不是修改脚本。

模式 2:

在此模式中,您的脚本被修改并使用fetch 代替 JSONP。因为当使用上述脚本时,我可以确认有时会出现与 CORS 相关的错误。所以作为另一种模式,我想建议使用fetch。当使用fetch 时,我可以确认没有发生与CORS 相关的错误。

反应方:App.js

从:
export default function FrmTable() {
  const jsonp = (url, callback) => {
    var callbackName = "jsonp_callback_" + Math.round(100000 * Math.random());
    window[callbackName] = function(data) {
      alert("Formulaire envoyé ");
      delete window[callbackName];
      document.body.removeChild(script);
      callback(data);
    };

    var script = document.createElement("script");
    script.src =
      url + (url.indexOf("?") >= 0 ? "&" : "?") + "callback=" + callbackName;
    document.body.appendChild(script);
  };

  const mySubmitHandler = event => {
    event.preventDefault();
    /*    const request = new XMLHttpRequest();
        const formData = new FormData();
        formData.append("La FAQ en question", form.faqName);
        formData.append("Suggestion contenu pdf", form.faqSuggest);
        formData.append("Commentaires", form.faqComment);
        request.open("POST", formUrl);
        request.send(formData); */

    jsonp(formUrl + "&Commentaires=" + encodeURIComponent(faqComment), data => {
      // alert(data);
    });
    event.target.reset();
  };
到:
export default function FrmTable() {
  const mySubmitHandler = event => {
    event.preventDefault();
    fetch(formUrl + "?Commentaires=" + encodeURIComponent(faqComment))
      .then(res => res.text())
      .then(res => console.log(res))
      .catch(err => console.error(err));

    event.target.reset();
  };
  • 在这种情况下,也可以使用res.json() 代替res.text()

Google Apps 脚本方面:

当您想使用您的共享电子表格时,请进行如下修改。

从:
var lock = LockService.getPublicLock();
到:
var lock = LockService.getDocumentLock();

还有,

从:
   var callback = e.parameter.callback;
   return ContentService.createTextOutput(callback+'('+ JSON.stringify({"result":"success", "row": nextRow})+')').setMimeType(ContentService.MimeType.JAVASCRIPT);
} catch(error){
  var callback = e.parameter.callback;
   return ContentService.createTextOutput(callback+'('+ JSON.stringify({"result":"error", "error": error})+')').setMimeType(ContentService.MimeType.JAVASCRIPT);
} finally { //release lock
  lock.releaseLock();
}
到:
  return ContentService.createTextOutput(JSON.stringify({"result":"success", "row": nextRow})).setMimeType(ContentService.MimeType.JSON);
} catch(error){
  return ContentService.createTextOutput(JSON.stringify({"result":"error", "error": error})).setMimeType(ContentService.MimeType.JSON);
} finally { //release lock
  lock.releaseLock();
}

还有,

从:
var SHEET_NAME = "Feedback";
到:
var SHEET_NAME = "Sheet2";

在这种情况下,您也可以将工作表名称从Sheet2 修改为Feedback,而不是修改脚本。

注意:

  • 当您修改 Web Apps 的脚本时,请将 Web Apps 重新部署为新版本。这样,最新的脚本就会反映到 Web 应用程序中。请注意这一点。

参考资料:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-01-25
    • 1970-01-01
    • 1970-01-01
    • 2019-11-11
    • 2013-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多