[教學] 用 ChromeOS/Linux 開發 Google Apps Script WebApp 不用起 server 續篇

本帖最後由 javacomhk 於 2022-5-19 17:48 編輯

請首先參考這篇怎樣設定 React.js + Bootstrap 開發 Cloud 嘅 WebApp 教學,而現在呢一續篇是延續上一篇去講解 Google Apps Script WebApp 最精彩及重要的 backend 功能,就係可儲存及讀取在 Google Spreadsheet 嘅 data。與及在儲存 Spreadsheet data 後立即用 Gmail 服務去觸發 sendMail 功能。

(D) 第四部份,使用 React.js + Bootstrap 增加 Input Form 及 Validation Rules 另外修改網頁去顯示 Google Spreadsheet data。

(D1) 修改以下文件 ~/webappreact/src/App.js 內容為
  1. import { Routes, Route } from "react-router-dom"
  2. import Home from "./components/Home"
  3. import About from "./components/About"
  4. import Data from "./components/Data"
  5. import FormInput from "./components/FormInput"
  6. import Nav from "./components/Nav"

  7. function App() {
  8.   return <>
  9.       <Nav />
  10.       <Routes>
  11.         <Route path="/" element={<Home />} />
  12.         <Route path="/about" element={<About />} />
  13.         <Route path="/data" element={<Data />} />
  14.         <Route path="/form" element={<FormInput />} />
  15.         <Route path="*" element={<Home />} />
  16.       </Routes>
  17.     </>
  18. }
  19. export default App
複製代碼
(D2) 修改以下文件 ~/webappreact/src/components/Nav.js 內容為
  1. import { Link } from "react-router-dom"

  2. function Nav() {
  3.   return <>
  4.         <span className="css-nav"><Link to="/">Home</Link></span>
  5.         <span className="css-nav"><Link to="about">About</Link></span>
  6.         <span className="css-nav"><Link to="data">Data</Link></span>
  7.         <span className="css-nav"><Link to="form">Form</Link></span>
  8.   </>
  9. }

  10. export default Nav
複製代碼
(D3) 修改以下文件 ~/webappreact/src/components/Data.js 內容為
  1. import React, {useState, useEffect} from "react"
  2. import {Container, Table} from "react-bootstrap";

  3. function Data() {
  4.   const [jsonResults,setJsonResults] = useState(null)
  5.   const [data,setData] = useState(null)
  6.   const [loading, setLoading] = useState(false)
  7.   if (typeof google === 'object') {
  8.     useEffect(()=>{
  9.       setLoading(true)
  10.       google.script.run.withSuccessHandler(response => {
  11.         console.log(response)
  12.         const [...values] = JSON.parse(response);
  13.         const keys = ["firstname","lastname","country","email","date"];
  14.         const objects = values.map(array => array.reduce((a, v, i) => ({...a, [keys[i]]: v}), {}));
  15.         console.log(objects);
  16.         setData([...objects])
  17.         setLoading(false);
  18.       }).withFailureHandler(er => {
  19.         alert(er)
  20.       }).getSpreadData();
  21.     },[])
  22.   }
  23.   else {
  24.     return <>
  25.       <Container>
  26.       <div style={{"height": "30px"}}/>
  27.       <h1 className="h1">Test getData from localhost</h1>
  28.       <Table striped bordered hover>
  29.         <thead>
  30.           <tr>
  31.             <th>#</th>
  32.             <th>First Name</th>
  33.             <th>Last Name</th>
  34.             <th>Creation Date</th>
  35.           </tr>
  36.         </thead>
  37.         <tbody>
  38.           <tr>
  39.             <td>1</td>
  40.             <td>FirstName 1</td>
  41.             <td>LastName 1</td>
  42.             <td>20220516</td>
  43.           </tr>
  44.           <tr>
  45.             <td>2</td>
  46.             <td>FirstName 2</td>
  47.             <td>LastName 2</td>
  48.             <td>20220517</td>
  49.           </tr>
  50.         </tbody>
  51.       </Table>
  52.       </Container>
  53.     </>
  54.   }
  55.   if (loading) return <h1>loading...</h1>
  56.   if (!data) return null;
  57.   console.log(JSON.stringify(data))
  58.   return <>
  59.     <Container>
  60.     <div style={{"height": "30px"}}/>
  61.     <h1 className="h1">getData from Google SpreadSheet</h1>
  62.     <Table striped bordered hover>
  63.       <thead>
  64.         <tr>
  65.           <th>#</th>
  66.           <th>First Name</th>
  67.           <th>Last Name</th>
  68.           <th>Creation Date</th>
  69.         </tr>
  70.       </thead>
  71.       <tbody>
  72.         { data.map((row, index) => (
  73.           <tr key={index}>
  74.             <td>{index}</td>
  75.             <td>{row.firstname}</td>
  76.             <td>{row.lastname}</td>
  77.             <td>{row.date}</td>
  78.           </tr>
  79.           ))
  80.         }
  81.       </tbody>
  82.     </Table>
  83.     </Container>
  84.   </>
  85. }

  86. export default Data
複製代碼
(D4) 修改以下文件 ~/webappreact/src/apps-script/main.js 內容為
  1. function doGet() {
  2.   return HtmlService.createTemplateFromFile("index")
  3.   .evaluate()
  4.   .addMetaTag("viewport","width=device-width, initial-scale=1.0")
  5. }

  6. function getData() {
  7.   console.log("getData()")
  8.   return (
  9.     [{
  10.       "key": "apple",
  11.       "value": "green"
  12.      },
  13.      {   
  14.      "key": "banana",
  15.      "value": "yellow"
  16.      }]
  17.   );
  18. }

  19. function getSpreadData() {
  20.   const data = SpreadsheetApp
  21.       .getActiveSpreadsheet()
  22.       .getActiveSheet()
  23.       .getDataRange()
  24.       .getValues();
  25.   console.log("getSpreadData() "+ JSON.stringify(data))

  26.   return JSON.stringify(data)
  27. }

  28. function AddRecord(firstname, lastname, country, email) {
  29.   console.log("AddRecord() "+ email)
  30.   var webAppSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  31.   const datestr = (new Date().toLocaleString('sv',{timeZone: 'Asia/Hong_Kong'})).slice(0, 19).replace(/-/g, "").replace(/:/g, "").replace("T", " ");
  32.   webAppSheet.appendRow([firstname, lastname, country, email, datestr]);
  33.   // sendEmailfromdoc(firstname, lastname, email);
  34. }

  35. function sendEmailfromdoc(firstname, lastname, email){
  36.   // fill in google doc ID here
  37.   var id = 'FILL-IN-GOOGLE-DOC-ID-HERE' ;
  38.   var forDriveScope = DriveApp.getStorageUsed(); //needed to get Drive Scope requested
  39.   var url = "https://docs.google.com/feeds/download/documents/export/Export?id="+id+"&exportFormat=html";
  40.   var param = {
  41.     method      : "get",
  42.     headers     : {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},
  43.     muteHttpExceptions:true,
  44.   };
  45.   var html = UrlFetchApp.fetch(url,param).getContentText();
  46. // email send out with replaced fields
  47.   var htmlout1 = html.replace("{{title_1}}","Welcome to join our Newsletter subscription");
  48.   htmlout1 = htmlout1.replace("{{heading_1}}","Dear "+ firstname +" "+lastname);
  49.   htmlout1 = htmlout1.replace("{{heading_2}}","You have subscribed our Newsletter successfully.");
  50. //  Logger.log(htmlout1);
  51.   MailApp.sendEmail({
  52.     to: email,
  53.     subject: "Newsletter Registration",
  54.     htmlBody: htmlout1
  55.     });
  56.   console.log('email sent to '+ email + ' !!')
  57. }
複製代碼
(D5) 新增以下文件 ~/webappreact/src/components/FormInput.js 內容為
  1. 上傳在 … https://pastebin.com/nFWTgrdz
複製代碼
(D6)新增及修改完 Code 後可根據上一篇的(B8)的方法執行 npm run build 及 npm run start 後在 localhost 測試

(D7) 新增及修改完 Code 後可根據上一篇的(B9) 的方法執行 npm run build 及 npm run gpush 後預備在 Google WebApp 測試,接著便可用 Browser 打入Test Development 的Web URL 去測試 Input Form,今次主要測試Form 內的 Registration Form Input,成功後如下截圖。




(E) 第五部份,製作 email template 及修改 Google Apps Script Code 去測試 Gmail 服務

(E1) 用 Chrome Browser在 drive.google.com 新增一個 Google doc 作為 email template ,內容如下圖,具備有 {{title_1}}  {{heading_1}}   {{heading_2}}  等字串及或附加任何圖片或文字格式。


(E2) 在 Browser 網址欄中取得當時這個 Google doc 的 ID 字串(如下圖)。

(E3) 將這個 ID 貼在 ~/webappreact/src/apps-script/main.js 檔的相應位置(如下圖)及同時 uncomment (即去除最開頭嘅 // 字符) 這句 sendEmailfromdoc 代碼讓 input form save data 後隨即執行 sendMail 功能。






(E4) 修改完 Code 後可根據上一篇的(B9) 方法執行 npm run build 及 npm run gpush 後預備在 Google WebApp 測試。

(E5) Browser 在 drive.google.com 打開 React Test Project ,進入 Apps Script Editor 透過執行任何 function 例如 getData() ,便會激發要求去授權取得 Authorisation token 讀取 Google doc 及可以去 send mail 。

(E6) 接著便可使用 Test Development 的 Web URL 去測試 Input Form 內輸入你自己的電郵地址去登記,看看是否可以收到郵件如下截圖。
附件: 您需要登錄才可以下載或查看附件。沒有帳號?註冊

本帖最後由 javacomhk 於 2022-5-19 17:35 編輯

留位

TOP

本帖最後由 javacomhk 於 2022-5-19 17:36 編輯

留位

TOP