I am trying to add dropdown list dynamically to a WebApp using google appscript. I wrote a few lines of javascript code in the client side to communicate the server side to fetch the data from google-sheets. After a lot of trying, I'm somewhat successful. However, it looks like, whenever I click on the "Add Product" button, for first 1-2 times the array from which the dropdown is generated is empty. As a result the dropdown remains blank. However after 1 or 2 blank dropdowns the it starts working as it's suppose to.
What am I doing wrong ?
I have 3 files-
form.html
code.gs
js_script.html
Link to the google sheet
Content of form.html-
<body>
<div class="container">
<div class = "row">
<h1>Order Form</h2>
</div> <!-- end of row -->
<div class = "row">
<input id="orderno" type="text" class="validate">
<label for="orderno">Order Number</label>
</div> <!-- end of row -->
<div class = "row">
<input id="clientname" type="text" class="validate">
<label for="clientname">Client Name</label>
</div> <!-- end of row -->
<div class = "row">
<input id="clientaddr" type="text" class="validate">
<label for="clientaddr">Client Address</label>
</div> <!-- end of row -->
<div class = "row">
<input id="clientphone" type="text" class="validate">
<label for="clientphone">Client Phone Number</label>
</div> <!-- end of row -->
<div class = "row">
<input id="ordertype" type="text" class="validate">
<label for="ordertype">Order Type</label>
</div> <!-- end of row -->
<div id="productsection"></div>
<div class = "row">
<button id="addproduct">Add Product</button>
</div> <!-- end of row -->
<div class = "row">
<button id="submitBtn">Submit</button>
</div> <!-- end of row -->
</div> <!-- End of "container" class -->
<?!= include("js_script"); ?>
</body>
Content of code.gs
const ssID = "1YKZYgKctsXU3DKTidVVPUhmPXUkzjjocaiMz1S76JAE";
const ss = SpreadsheetApp.openById(ssID);
function doGet(e){
Logger.log(e);
return HtmlService.createTemplateFromFile("form").evaluate();
}
function include(fileName){
return HtmlService.createHtmlOutputFromFile(fileName).getContent();
}
function appendDataToSheet(userData){
const ws = ss.getSheetByName("orders");
ws.appendRow([new Date(), userData.orderNumber, userData.clientName, userData.clientAddress, userData.clientPhone, userData.orderType, userData.products].flat());
}
function getOptionArray(){
const ws = ss.getSheetByName("product_list");
const optionList = ws.getRange(2, 1, ws.getRange("A2").getDataRegion().getLastRow() - 1).getValues()
.map(item => item[0]);
return optionList;
}
function logVal(data){
Logger.log(data);
}
Content of js_script.html
<script>
let counter = 0;
let optionList = [];
document.getElementById("submitBtn").addEventListener("click", writeDataToSheet);
document.getElementById("addproduct").addEventListener("click", addInputField);
function addInputField(){
counter++;
// The idea is, everytime when "add product" button is clicked, the following element must be added to the "<div id="productoption></div>" tag.
// <div class="row">
// <select id="productX">
// <option>option-X</option>
// </select>
// </div>
const newDivTag = document.createElement('div');
const newSelectTag = document.createElement('select');
newDivTag.class = "row";
newSelectTag.id = "product" + counter.toString();
google.script.run.withSuccessHandler(updateOptionList).getOptionArray();
google.script.run.logVal(optionList); // This is just to test the optionList array if it's updated or not
for(let i = 0; i < optionList.length; i++){
const newOptionTag = document.createElement('option');
newOptionTag.textContent = optionList[i];
newOptionTag.value = optionList[i];
newSelectTag.appendChild(newOptionTag);
}
newDivTag.appendChild(newSelectTag);
document.getElementById('productsection').appendChild(newDivTag);
}
function writeDataToSheet(){
const userData = {};
userData.orderNumber = document.getElementById("orderno").value;
userData.clientName = document.getElementById("clientname").value;
userData.clientAddress = document.getElementById("clientaddr").value;
userData.clientPhone = document.getElementById("clientphone").value;
userData.orderType = document.getElementById("ordertype").value;
userData.products = [];
for(let i = 0; i < counter; i++) {
let input_id = "product" + (i+1).toString();
userData.products.push(document.getElementById(input_id).value);
}
google.script.run.appendDataToSheet(userData);
}
function updateOptionList(arr){
optionList = arr.map(el => el);
}
</script>
About your current issue of However, it looks like, whenever I click on the "Add Product" button, for first 1-2 times the array form which the dropdown is generated is empty. As a result the dropdown remains blank. However after 1 or 2 blank dropdowns the it starts working as it's suppose to., when I saw your script, I thought that the reason for your issue might be due to that google.script.run is run with the asynchronous process. If my understanding is correct, how about the following modification?
In this case, your js_script.html is modified.
Modified script:
<script>
let counter = 0;
// let optionList = []; // Removed
document.getElementById("submitBtn").addEventListener("click", writeDataToSheet);
document.getElementById("addproduct").addEventListener("click", addInputField);
// Modified
function addInputField(){
counter++;
const newDivTag = document.createElement('div');
const newSelectTag = document.createElement('select');
newDivTag.class = "row";
newSelectTag.id = "product" + counter.toString();
google.script.run.withSuccessHandler(arr => {
const optionList = updateOptionList(arr);
google.script.run.logVal(optionList);
for(let i = 0; i < optionList.length; i++){
const newOptionTag = document.createElement('option');
newOptionTag.textContent = optionList[i];
newOptionTag.value = optionList[i];
newSelectTag.appendChild(newOptionTag);
}
newDivTag.appendChild(newSelectTag);
document.getElementById('productsection').appendChild(newDivTag);
}).getOptionArray();
}
function writeDataToSheet(){
const userData = {};
userData.orderNumber = document.getElementById("orderno").value;
userData.clientName = document.getElementById("clientname").value;
userData.clientAddress = document.getElementById("clientaddr").value;
userData.clientPhone = document.getElementById("clientphone").value;
userData.orderType = document.getElementById("ordertype").value;
userData.products = [];
for(let i = 0; i < counter; i++) {
let input_id = "product" + (i+1).toString();
userData.products.push(document.getElementById(input_id).value);
}
google.script.run.appendDataToSheet(userData);
}
// Modified
function updateOptionList(arr){
return arr.map(el => el); // I cannot understand this mean.
}
</script>
or, in this case, updateOptionList might not be required to be used as follows.
<script>
let counter = 0;
// let optionList = []; // Removed
document.getElementById("submitBtn").addEventListener("click", writeDataToSheet);
document.getElementById("addproduct").addEventListener("click", addInputField);
// Modified
function addInputField(){
counter++;
const newDivTag = document.createElement('div');
const newSelectTag = document.createElement('select');
newDivTag.class = "row";
newSelectTag.id = "product" + counter.toString();
google.script.run.withSuccessHandler(optionList => {
google.script.run.logVal(optionList);
for(let i = 0; i < optionList.length; i++){
const newOptionTag = document.createElement('option');
newOptionTag.textContent = optionList[i];
newOptionTag.value = optionList[i];
newSelectTag.appendChild(newOptionTag);
}
newDivTag.appendChild(newSelectTag);
document.getElementById('productsection').appendChild(newDivTag);
}).getOptionArray();
}
function writeDataToSheet(){
const userData = {};
userData.orderNumber = document.getElementById("orderno").value;
userData.clientName = document.getElementById("clientname").value;
userData.clientAddress = document.getElementById("clientaddr").value;
userData.clientPhone = document.getElementById("clientphone").value;
userData.orderType = document.getElementById("ordertype").value;
userData.products = [];
for(let i = 0; i < counter; i++) {
let input_id = "product" + (i+1).toString();
userData.products.push(document.getElementById(input_id).value);
}
google.script.run.appendDataToSheet(userData);
}
</script>
Note:
When you modified the Google Apps Script of Web Apps, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful about this.
You can see the detail of this in my report "Redeploying Web Apps without Changing URL of Web Apps for new IDE (Author: me)".
Reference:
Class google.script.run (Client-side API)
I've looked at previous questions like this and cannot find the answer to my problem. I am working in javascript creating a checkout screen and I have two onclicks for two different html files but when I go to the html file for both it says that the other onclick is null. I have tried window.load and moving the script to the bottom of the
var cart = [];
var search = document.getElementById("addItem");
let placement = 0;
var cartElement = document.getElementById("showCart");
var cartTotal = document.getElementById("totalCart");
search.onclick = function(e) {
var userInput = document.getElementById("query").value;
var cartHTML = "";
e.preventDefault();
placement = 0;
for (i = 0; i < menu.length; i++) {
if (menu[i][0].includes(userInput)) {
cart.push(menu[i]);
placement++;
}
}
if (placement == 0) {
alert("Menu option not included. Please try again.");
}
cart.forEach((item, Order) => {
var cartItem = document.createElement("span");
cartItem.textContent = item[0] + " (" + item[1] + ")";
cartHTML += cartItem.outerHTML;
});
cartElement.innerHTML = cartHTML;
}
window.onload = function() {
var checkout = document.getElementById("addCartButton");
checkout.onclick = function(event) {
cart.forEach()
var cartTotalHTML = "";
event.preventDefault();
cart.forEach(Item, Order => {
var totalInCart = 0;
var writeCart = document.createElement("span");
totalInCart += Order[1];
});
writeCart.textContent = cartTotal += item[1];
cartTotalHTML = writeCart.outerHTML;
cartTotal.innerHTML = cartTotalHTML;
console.log(cartTotal);
}
}
<h3>Search for items in the menu below to add to cart</h3>
<form id="searchMenu">
<input type="search" id="query" name="q" placeholder="Search Menu..."></inpuut>
<input type = "Submit" name= "search" id="addItem" ></input>
</form>
<h4>Your cart: </h4>
<div class="Cart">
<div id="showCart"></div>
</div>
<script src="Script.js"></script>
<h4>Cart</h4>
<button id='addCartButton' class="Cart">Add Cart</button>
<div class="ShowCart">
<div id="totalCart"></div>
</div>
<script src="Script.js"></script>
I am a beginner in google app script. I am creating resident payment system where if the user increases the number of paid by one I want to add the a new row in sales worksheet in google sheet with new invoice number and the address of the specific user (which means lets say the user's address is 16, Jalan Sanggul 4 so I want to add it to the worksheet under the address column). I have attached few images and link to my google appscript coding here to explain myself better. I really hope that you guys can help me with it. Thank you guys.
Link to google app script (There is few numbers of html there so give the link ) - https://script.google.com/d/1NvaCQBc1fZWT7Y5cdvSi_XVL2-ytRVDP41AgY-btOA_MYrd8vcQK4fb_/edit?usp=sharing
Link to google sheet - https://docs.google.com/spreadsheets/d/1GO_kQVuXBtBZSKreNSvYu_kTRQMuL7YN5VXd14zgMig/edit?usp=sharing
[Payment Data Image (1) - Google Sheet]1
[Payment Data Image (2)- Google Sheet]2
Code.gs
function doGet(request) {
return HtmlService.createTemplateFromFile('Index').evaluate()
.setTitle("Admin Login")
.addMetaTag('viewport', 'width=device-width, initial-scale=1')
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
//Check Login
function checkLogin(loginUsername, loginPassword) {
var url = 'https://docs.google.com/spreadsheets/d/1bM8l6JefFsPrlJnTWf56wOhnuSjdIwg3hMbY1tN1Zp8/edit#gid=1151242353';
var ss= SpreadsheetApp.openByUrl(url);
var webAppSheet = ss.getSheetByName("Admin Data");
var getLastRow = webAppSheet.getLastRow();
var found_record = '';
for(var i = 1; i <= getLastRow; i++)
{
if(webAppSheet.getRange(i, 1).getValue() == loginUsername &&
webAppSheet.getRange(i, 2).getValue() == loginPassword)
{
found_record = 'TRUE';
}
}
if(found_record == '')
{
found_record = 'FALSE';
}
return found_record;
}
/*--------------------------------------------------------------------------------------------------------
---------------GLOBAL VARIABLES---------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------
*/
function globalVariables(){
var varArray = {
spreadsheetId : '1bM8l6JefFsPrlJnTWf56wOhnuSjdIwg3hMbY1tN1Zp8', //** CHANGE !!!
admindataRage : 'Admin Data!A2:B', //** CHANGE !!!
adminidRange : 'Admin Data!A2:A', //** CHANGE !!!
adminlastCol : 'B', //** CHANGE !!!
admininsertRange : 'Admin Data!A1:B1', //** CHANGE !!!
adminsheetID : '1151242353',
dataRage : 'USERNAMES!A2:H', //** CHANGE !!!
idRange : 'USERNAMES!A2:A', //** CHANGE !!!
lastCol : 'H', //** CHANGE !!!
insertRange : 'USERNAMES!A1:H1', //** CHANGE !!!
sheetID : '0'
};
return varArray;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------FUCNTION FOR ADMIN CRUD PART-----------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------------------------------------------
*/
/* PROCESS FORM */
function processAdminForm(formObject){
if(formObject.admin_uname && checkAdminID(formObject.admin_uname)){//Execute if form passes an ID and if is an existing ID
updateAdminData(getAdminFormValues(formObject),globalVariables().spreadsheetId,getAdminRangeByID(formObject.admin_uname)); // Update Data
}else{ //Execute if form does not pass an ID
appendAdminData(getAdminFormValues(formObject),globalVariables().spreadsheetId,globalVariables().admininsertRange); //Append Form Data
}
return getAdminLastTenRows();//Return last 10 rows
}
/* GET FORM VALUES AS AN ARRAY */
function getAdminFormValues(formObject){
/* ADD OR REMOVE VARIABLES ACCORDING TO YOUR FORM*/
if(formObject.admin_uname && checkAdminID(formObject.admin_uname)){
var values = [[
//formObject.username.toString(),
formObject.admin_uname,
formObject.admin_pw]];
}else{
var values = [[
//new Date().getTime().toString(),//https://webapps.stackexchange.com/a/51012/244121
formObject.admin_uname,
formObject.admin_pw]];
}
return values;
}
/* CREATE/ APPEND DATA */
function appendAdminData(values, spreadsheetId,range){
var valueRange = Sheets.newRowData();
valueRange.values = values;
var appendRequest = Sheets.newAppendCellsRequest();
appendRequest.adminsheetID = spreadsheetId;
appendRequest.rows = valueRange;
var results = Sheets.Spreadsheets.Values.append(valueRange, spreadsheetId, range,{valueInputOption: "RAW"});
}
/* READ DATA */
function readAdminData(spreadsheetId,range){
var result = Sheets.Spreadsheets.Values.get(spreadsheetId, range);
return result.values;
}
/* UPDATE DATA */
function updateAdminData(values,spreadsheetId,range){
var valueRange = Sheets.newValueRange();
valueRange.values = values;
var result = Sheets.Spreadsheets.Values.update(valueRange, spreadsheetId, range, {
valueInputOption: "RAW"});
}
/*DELETE DATA*/
function deleteAdminData(ID){
//https://developers.google.com/sheets/api/guides/batchupdate
//https://developers.google.com/sheets/api/samples/rowcolumn#delete_rows_or_columns
var startIndex = getAdminRowIndexByID(ID);
var deleteRange = {
"sheetId" : globalVariables().adminsheetID,
"dimension" : "ROWS",
"startIndex" : startIndex,
"endIndex" : startIndex+1
}
var deleteRequest= [{"deleteDimension":{"range":deleteRange}}];
Sheets.Spreadsheets.batchUpdate({"requests": deleteRequest}, globalVariables().spreadsheetId);
return getAdminLastTenRows();//Return last 10 rows
}
/* CHECK FOR EXISTING ID, RETURN BOOLEAN */
function checkAdminID(ID){
var idList = readAdminData(globalVariables().spreadsheetId,globalVariables().adminidRange,).reduce(function(a,b){return a.concat(b);});
return idList.includes(ID);
}
/* GET DATA RANGE A1 NOTATION FOR GIVEN ID */
function getAdminRangeByID(id){
if(id){
var idList = readAdminData(globalVariables().spreadsheetId,globalVariables().adminidRange);
for(var i=0;i<idList.length;i++){
if(id==idList[i][0]){
return 'Admin Data!A'+(i+2)+':'+globalVariables().adminlastCol+(i+2);
}
}
}
}
/* GET RECORD BY ID */
function getAdminRecordById(id){
if(id && checkAdminID(id)){
var result = readAdminData(globalVariables().spreadsheetId,getAdminRangeByID(id));
return result;
}
}
/* GET ROW NUMBER FOR GIVEN ID */
function getAdminRowIndexByID(id){
if(id){
var idList = readAdminData(globalVariables().spreadsheetId,globalVariables().adminidRange);
for(var i=0;i<idList.length;i++){
if(id==idList[i][0]){
var rowIndex = parseInt(i+1);
return rowIndex;
}
}
}
}
/*GET LAST 10 RECORDS */
function getAdminLastTenRows(){
var lastRow = readAdminData(globalVariables().spreadsheetId,globalVariables().admindataRage).length+1;
if(lastRow<=11){
var range = globalVariables().admindataRage;
}else{
var range = 'Admin Data!A'+(lastRow-9)+':'+globalVariables().adminlastCol;
}
var lastTenRows = readAdminData(globalVariables().spreadsheetId,range);
return lastTenRows;
}
/* GET ALL RECORDS */
function getAdminAllData(){
var data = readAdminData(globalVariables().spreadsheetId,globalVariables().admindataRage);
return data;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------FUCNTION FOR RESIDENT CRUD PART-----------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------------------------------------------
*/
/* PROCESS FORM */
function processForm(formObject){
if(formObject.username && checkID(formObject.username)){//Execute if form passes an ID and if is an existing ID
updateData(getFormValues(formObject),globalVariables().spreadsheetId,getRangeByID(formObject.username)); // Update Data
}else{ //Execute if form does not pass an ID
appendData(getFormValues(formObject),globalVariables().spreadsheetId,globalVariables().insertRange); //Append Form Data
}
return getLastTenRows();//Return last 10 rows
}
/* GET FORM VALUES AS AN ARRAY */
function getFormValues(formObject){
/* ADD OR REMOVE VARIABLES ACCORDING TO YOUR FORM*/
if(formObject.username && checkID(formObject.username)){
var values = [[
//formObject.username.toString(),
formObject.username,
formObject.housenumber,
formObject.street,
formObject.firstname,
formObject.lastname,
formObject.noOfPaid,
formObject.password,
formObject.phone]];
}else{
var values = [[
//new Date().getTime().toString(),//https://webapps.stackexchange.com/a/51012/244121
formObject.username,
formObject.housenumber,
formObject.street,
formObject.firstname,
formObject.lastname,
formObject.noOfPaid,
formObject.password,
formObject.phone]];
}
return values;
}
/* CREATE/ APPEND DATA */
function appendData(values, spreadsheetId,range){
var valueRange = Sheets.newRowData();
valueRange.values = values;
var appendRequest = Sheets.newAppendCellsRequest();
appendRequest.sheetID = spreadsheetId;
appendRequest.rows = valueRange;
var results = Sheets.Spreadsheets.Values.append(valueRange, spreadsheetId, range,{valueInputOption: "RAW"});
}
/* READ DATA */
function readData(spreadsheetId,range){
var result = Sheets.Spreadsheets.Values.get(spreadsheetId, range);
return result.values;
}
/* UPDATE DATA */
function updateData(values,spreadsheetId,range){
var valueRange = Sheets.newValueRange();
valueRange.values = values;
var result = Sheets.Spreadsheets.Values.update(valueRange, spreadsheetId, range, {
valueInputOption: "RAW"});
}
/*DELETE DATA*/
function deleteData(ID){
//https://developers.google.com/sheets/api/guides/batchupdate
//https://developers.google.com/sheets/api/samples/rowcolumn#delete_rows_or_columns
var startIndex = getRowIndexByID(ID);
var deleteRange = {
"sheetId" : globalVariables().sheetID,
"dimension" : "ROWS",
"startIndex" : startIndex,
"endIndex" : startIndex+1
}
var deleteRequest= [{"deleteDimension":{"range":deleteRange}}];
Sheets.Spreadsheets.batchUpdate({"requests": deleteRequest}, globalVariables().spreadsheetId);
return getLastTenRows();//Return last 10 rows
}
/* CHECK FOR EXISTING ID, RETURN BOOLEAN */
function checkID(ID){
var idList = readData(globalVariables().spreadsheetId,globalVariables().idRange,).reduce(function(a,b){return a.concat(b);});
return idList.includes(ID);
}
/* GET DATA RANGE A1 NOTATION FOR GIVEN ID */
function getRangeByID(id){
if(id){
var idList = readData(globalVariables().spreadsheetId,globalVariables().idRange);
for(var i=0;i<idList.length;i++){
if(id==idList[i][0]){
return 'USERNAMES!A'+(i+2)+':'+globalVariables().lastCol+(i+2);
}
}
}
}
/* GET RECORD BY ID */
function getRecordById(id){
if(id && checkID(id)){
var result = readData(globalVariables().spreadsheetId,getRangeByID(id));
return result;
}
}
/* GET ROW NUMBER FOR GIVEN ID */
function getRowIndexByID(id){
if(id){
var idList = readData(globalVariables().spreadsheetId,globalVariables().idRange);
for(var i=0;i<idList.length;i++){
if(id==idList[i][0]){
var rowIndex = parseInt(i+1);
return rowIndex;
}
}
}
}
/*GET LAST 10 RECORDS */
function getLastTenRows(){
var lastRow = readData(globalVariables().spreadsheetId,globalVariables().dataRage).length+1;
if(lastRow<=11){
var range = globalVariables().dataRage;
}else{
var range = 'USERNAMES!A'+(lastRow-9)+':'+globalVariables().lastCol;
}
var lastTenRows = readData(globalVariables().spreadsheetId,range);
return lastTenRows;
}
/* GET ALL RECORDS */
function getAllData(){
var data = readData(globalVariables().spreadsheetId,globalVariables().dataRage);
return data;
}
/*GET DROPDOWN LIST */
function getDropdownList(range){
var list = readData(globalVariables().spreadsheetId,range);
return list;
}
//CODE FOR DATA SEARCH
function searchData(formObject){
var result = [];
if(formObject.searchtext){//Execute if form passes search text
var data = Sheets.Spreadsheets.Values.get(globalVariables().spreadsheetId, globalVariables().dataRage).values;
for(var i=0;i<data.length;i++){
for(var j=0;j<data[i].length;j++){
if(data[i][j].toLowerCase().search(formObject.searchtext.toLowerCase())!=-1){
result.push(data[i])
}
}
}
}
return result;
}
/* INCLUDE HTML PARTS, EG. JAVASCRIPT, CSS, OTHER HTML FILES */
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename)
.getContent();
}
function getScriptURL() {
var url = ScriptApp.getService().getUrl();
return url ;
}
Index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js" integrity="sha384-xrRywqdh3PHs8keKZN+8zzc5TX0GRTLCcmivcbNJWm2rs5C8PRhcEn3czEjhAO9o" crossorigin="anonymous"></script>
<!--<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
-->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<!-- Bootstrap 3.3.6 -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<!-- Font Awesome -->
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
<link rel="stylesheet" href="path/to/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.7/css/all.css">
<?!= include('JavaScript'); ?> <!-- See JavaScript.html file -->
<?!= include('CSS'); ?> <!-- See CSS.html file -->
</head>
<body onload="createStreetDropdown()">
<div id="loginDisplay" class="loginDisplay">
<?!= include('loginDisplay'); ?>
</div>
<div style="display:none" id="dataDisplay" >
<div>
<button type="button" class="btn btn-primary" onclick="LogOut()">
Log out
</button>
<!--<button type="button" class="btn btn-default btn-md" onclick="LogOut()">
<span class="glyphicon glyphicon-log-out"></span>Log out
</button>-->
</div>
<br>
<div>
<button type="button" class="btn btn-primary" onclick="">
Admin
</button>
<button type="button" class="btn btn-primary" onclick="">
Resident
</button>
</div>
<div class="container">
<div class="row">
<div class="col-lg-6">
<?!= include('AdminForm'); ?>
<br><br>
<div id="output"></div>
</div>
<div class="col-lg-6">
<?!= include('AdminDataTable'); ?>
</div>
<!--
<div class="col-lg-12">
<?!= include('AdminDataTable'); ?>
</div>
-->
</div>
<br><br>
<hr>
<div class="row">
<div class="col-lg-12">
<?!= include('Form'); ?>
<br><br>
<div id="output"></div>
</div>
<div class="col-lg-12">
<?!= include('DataTable'); ?>
</div>
</div>
</div>
</div>
</body>
</html>
JavaScript.html
<script>
//--------Login Page----------------------------------
function LoginUser()
{
var loginUsername = document.getElementById("loginUsername").value;
var loginPassword = document.getElementById("loginPassword").value;
google.script.run.withSuccessHandler(function(output)
{
if(output == 'TRUE')
{
document.getElementById("loginDisplay").style.display = "none";
document.getElementById("dataDisplay").style.display = "block";
}
else if(output == 'FALSE')
{
document.getElementById("errorMessage").innerHTML = "Failed to Login";
}
}).checkLogin(loginUsername, loginPassword);
}
//-------Log Out----------------------
function LogOut(){
google.script.run
.withSuccessHandler(function(url){window.open(url,"_top");})
.getScriptURL();
//var url=window.location.href
//window.open(url,'_top');
//window.open("https://script.google.com/macros/s/AKfycbyS5LJn4i6mJeMzjWWryW7TUUDeeAZtVteJdCd6lT0FPPEiCiVy/exec",'_top');
}
//----------------------------------------------------------
//---------Admin Part---------------------------------------
// Prevent forms from submitting.
function preventAdminFormSubmit() {
var forms = document.querySelectorAll('form');
for (var i = 0; i < forms.length; i++) {
forms[i].addEventListener('submit', function(event) {
event.preventDefault();
});
}
}
window.addEventListener("load", adminfunctionInit, true);
//INITIALIZE FUNCTIONS ONLOAD
function adminfunctionInit(){
preventAdminFormSubmit();
getAdminLastTenRows();
};
//HANDLE FORM SUBMISSION
function handleAdminFormSubmit(formObject) {
google.script.run.withSuccessHandler(createAdminTable).processAdminForm(formObject);
document.getElementById("adminForm").reset();
}
//GET LAST 10 ROWS
function getAdminLastTenRows (){
google.script.run.withSuccessHandler(createAdminTable).getAdminLastTenRows();
}
//GET ALL DATA
function getAdminAllData(){
//document.getElementById('adminDataTable').innerHTML = "";
google.script.run.withSuccessHandler(createAdminTable).getAdminAllData();
}
//CREATE THE DATA TABLE
function createAdminTable(dataArray) {
if(dataArray){
var result = "<table class='table table-sm' style='font-size:0.8em'>"+
"<thead style='white-space: nowrap'>"+
"<tr>"+ //Change table headings to match witht he Google Sheet
"<th scope='col'>Delete</th>"+
"<th scope='col'>Edit</th>"+
"<th scope='col'>Username</th>"+
"<th scope='col'>Password</th>"+
"</tr>"+
"</thead>";
for(var i=0; i<dataArray.length; i++) {
result += "<tr>";
result += "<td><button type='button' class='btn btn-danger btn-xs deleteBtn' onclick='deleteAdminData(this);'>Delete</button></td>";
result += "<td><button type='button' class='btn btn-warning btn-xs editBtn' onclick='editAdminData(this);'>Edit</button></td>";
for(var j=0; j<dataArray[i].length; j++){
result += "<td>"+dataArray[i][j]+"</td>";
}
result += "</tr>";
}
result += "</table>";
var div = document.getElementById('adminDataTable');
div.innerHTML = result;
document.getElementById("admin_message").innerHTML = "";
}else{
var div = document.getElementById('adminDataTable');
div.innerHTML = "Data not found!";
}
}
//DELETE DATA
function deleteAdminData(el) {
var result = confirm("Want to delete?");
if (result) {
var admin_uname = el.parentNode.parentNode.cells[2].innerHTML;
google.script.run.withSuccessHandler(createAdminTable).deleteAdminData(admin_uname);
}
}
//EDIT DATA
function editAdminData(el){
var admin_uname = el.parentNode.parentNode.cells[2].innerHTML; //https://stackoverflow.com/a/32377357/2391195
google.script.run.withSuccessHandler(populateAdminForm).getAdminRecordById(admin_uname);
}
//POPULATE FORM
function populateAdminForm(records){
document.getElementById('admin_uname').value = records[0][0];
document.getElementById('admin_pw').value = records[0][1];
document.getElementById("admin_message").innerHTML = "<div class='alert alert-warning' role='alert'>Update Record [ADMIN: "+records[0][0]+"]</div>";
}
//-----------------------------------------------------------------------
//----------------Resident Part------------------------------------------
// Prevent forms from submitting.
function preventFormSubmit() {
var forms = document.querySelectorAll('form');
for (var i = 0; i < forms.length; i++) {
forms[i].addEventListener('submit', function(event) {
event.preventDefault();
});
}
}
window.addEventListener("load", functionInit, true);
//INITIALIZE FUNCTIONS ONLOAD
function functionInit(){
preventFormSubmit();
getLastTenRows();
};
//HANDLE FORM SUBMISSION
function handleFormSubmit(formObject) {
google.script.run.withSuccessHandler(createTable).processForm(formObject);
document.getElementById("myForm").reset();
}
//GET LAST 10 ROWS
function getLastTenRows (){
google.script.run.withSuccessHandler(createTable).getLastTenRows();
}
//GET ALL DATA
function getAllData(){
//document.getElementById('dataTable').innerHTML = "";
google.script.run.withSuccessHandler(createTable).getAllData();
}
//CREATE THE DATA TABLE
function createTable(dataArray) {
if(dataArray){
var result = "<table class='table table-sm' style='font-size:0.8em'>"+
"<thead style='white-space: nowrap'>"+
"<tr>"+ //Change table headings to match witht he Google Sheet
"<th scope='col'>Delete</th>"+
"<th scope='col'>Edit</th>"+
//"<th scope='col'>ID</th>"+
"<th scope='col'>Username</th>"+
"<th scope='col'>House Number</th>"+
"<th scope='col'>Street</th>"+
"<th scope='col'>First Name</th>"+
"<th scope='col'>Last Name</th>"+
"<th scope='col'>No of Paid</th>"+
"<th scope='col'>Password</th>"+
"<th scope='col'>Phone</th>"+
"</tr>"+
"</thead>";
for(var i=0; i<dataArray.length; i++) {
result += "<tr>";
result += "<td><button type='button' class='btn btn-danger btn-xs deleteBtn' onclick='deleteData(this);'>Delete</button></td>";
result += "<td><button type='button' class='btn btn-warning btn-xs editBtn' onclick='editData(this);'>Edit</button></td>";
for(var j=0; j<dataArray[i].length; j++){
result += "<td>"+dataArray[i][j]+"</td>";
}
result += "</tr>";
}
result += "</table>";
var div = document.getElementById('dataTable');
div.innerHTML = result;
document.getElementById("message").innerHTML = "";
}else{
var div = document.getElementById('dataTable');
div.innerHTML = "Data not found!";
}
}
//DELETE DATA
function deleteData(el) {
var result = confirm("Want to delete?");
if (result) {
var username = el.parentNode.parentNode.cells[2].innerHTML;
google.script.run.withSuccessHandler(createTable).deleteData(username);
}
}
//EDIT DATA
function editData(el){
var username = el.parentNode.parentNode.cells[2].innerHTML; //https://stackoverflow.com/a/32377357/2391195
google.script.run.withSuccessHandler(populateForm).getRecordById(username);
}
//POPULATE FORM
function populateForm(records){
document.getElementById('username').value = records[0][0];
document.getElementById('housenumber').value = records[0][1];
document.getElementById('street').value = records[0][2];
document.getElementById('firstname').value = records[0][3];
document.getElementById('lastname').value = records[0][4];
document.getElementById('noOfPaid').value = records[0][5];
document.getElementById('password').value = records[0][6];
document.getElementById('phone').value = records[0][7];
document.getElementById("message").innerHTML = "<div class='alert alert-warning' role='alert'>Update Record [ID: "+records[0][0]+"]</div>";
}
//RETRIVE DATA FROM GOOGLE SHEET FOR STREET DROPDOWN
function createStreetDropdown() {
//SUBMIT YOUR DATA RANGE FOR DROPDOWN AS THE PARAMETER
google.script.run.withSuccessHandler(streetDropDown).getDropdownList("Configuration!A2:A11");
}
//POPULATE STREET DROPDOWNS
function streetDropDown(values) { //Ref: https://stackoverflow.com/a/53771955/2391195
var list = document.getElementById('street');
for (var i = 0; i < values.length; i++) {
var option = document.createElement("option");
option.value = values[i];
option.text = values[i];
list.appendChild(option);
}
}
//SEARCH DATA
function handleSearchForm(formObject) {
google.script.run.withSuccessHandler(createTable).searchData(formObject);
document.getElementById("search-form").reset();
}
</script>
AdminForm.html
<form id="adminForm" class="p-2 border border-light rounded bg-light" onsubmit="handleAdminFormSubmit(this)">
<p class="h4 mb-4 text-center">Admin Form</p>
<div id="admin_message"></div>
<input type="text" id="admin_message" name="admin_message" value="" style="display: none">
<div class="form-group">
<label for="admin_uname" >Username</label>
<input type="text" class="form-control" id="admin_uname" name="admin_uname" placeholder="Username" required>
</div>
<div class="form-group">
<label for="admin_pw" >Password</label>
<input type="password" class="form-control" id="admin_pw" name="admin_pw" placeholder="Password" required>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
<input class="btn btn-secondary" type="reset" value="Reset">
</form>
I have a web app that uses OMDB API which fetches all the related results that matches the title of a Movie searched by the user.
So for example the user searched for "Star Wars" the API will then return 483 results, I managed to make a pagination for it but it shows all the pages from 1-48 and what I'm trying to figure out is how can I only show pages [1,2,3,4,5,6,7...49(ending page)], then change that pagination to [2,3,4,5,6,7,8...49] and so on.
Heres the code for it:
<input type="text" class="form-control" id="title" name="title" placeholder="Movie Title...">
<button onclick="callOMDB(document.getElementById('title').value)" class="btn btn-primary">Search</button>
<div id="page" class="page">
<nav aria-label="Page navigation example">
<ul class="pagination" id="pagination">
</ul>
</nav>
</div>
<div id="info">
</div>
function callOMDB(x){
var poster = document.getElementById("info");
var page = document.getElementById("pagination");
var search = x;
var searchLink = 'https://www.omdbapi.com/?i=tt3896198&apikey=123456&s='+encodeURI(x);
$.getJSON(searchLink).then(function(response){
poster.innerHTML = '';
page.innerHTML = '';
var length = response.Search.length;
for(var i = 0; i < length; i++){
var yr = response.Search[i].Year;
var title = response.Search[i].Title;
poster.innerHTML += "<p>Year: "+yr+"</p><p>Title: "+title+"</p>";
}
var pageNo = response.totalResults/10;
var i = 0;
for(i = 1; i < pageNo; i++){
page.innerHTML += '<li class="page-item"><a onclick="nextPage('+i+',\''+search+'\')" class="page-link" href="#">'+i+'</a></li>';
}
});
}
You can use slice to get slice of the entire result and display that. For the purpose of demo I just shown you only. and results are just numbers. The array can be anything. Important things is slice to get parts of results you want and display them.
After as you click through you move the start and end positions
const resultsList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // This could be results / or page numbers
const maxResults = 9;
let upperPageIndex = maxResults;
let currentPageIndex = 0;
let resultsToDisplay = resultsList.slice(currentPageIndex , upperPageIndex); // slice(startIndex, endIndex);
console.log(resultsToDisplay)
// if currentPageIndex = 2 then show 2 more as you go through
upperPageIndex += 1; // You can make 2 a constant if needed
currentPageIndex += 1;
resultsToDisplay = resultsList.slice(currentPageIndex , upperPageIndex);
console.log(resultsToDisplay)
I used the paramter page in the omdbapi and that is my code
JS
window.addEventListener('DOMContentLoaded', function () {
const posterContainer = document.getElementById("info");
const pageContainer = document.getElementById("pagination");
const titleInput = document.getElementById('title');
const searchBtn = document.getElementById('searchBtn');
const searchLink = 'https://www.omdbapi.com/?i=tt3896198&apikey=56fbcd03';
searchBtn.addEventListener('click', () => {
getData(titleInput.value);
});
// event delegation
$('body').on('click', '.page-link', function (e) {
e.preventDefault();
getData(titleInput.value, this.id);
});
function getData(keyword, page = 1) {
$.getJSON(`${searchLink}&page=${page}&s=${keyword}`).then(function (response) {
posterContainer.innerHTML = '';
pageContainer.innerHTML = '';
var length = response.Search.length;
for (var i = 0; i < length; i++) {
var yr = response.Search[i].Year;
var title = response.Search[i].Title;
posterContainer.innerHTML += "<p>Year: " + yr + "</p><p>Title: " + title + "</p>";
}
var pageNo = response.totalResults / 10;
var i = 0;
for (i = 1; i < pageNo; i++) {
pageContainer.innerHTML += `<li class="page-item" ><a class="page-link" id="${i}" href="#">${i}</a></li>`;
}
});
}
});
HTML
<input type="text" class="form-control" id="title" name="title" placeholder="Movie Title...">
<button id="searchBtn" class="btn btn-primary">Search</button>
<div id="page" class="page">
<nav aria-label="Page navigation example">
<ul class="pagination" id="pagination">
</ul>
</nav>
</div>
<div id="info">
</div>
I am learning to use the "Article Search API" provided by the New York Times to allow my web application to display news articles based on the search results. I followed the tutorial at https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs and used their javascript code as closely as I could. The issue is that when I clicked on the 'submit search' button, my web-developer's console on Firefox showed me the message:
"Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://nyt-prod.apigee.net/svc/search/v2/articlesearch.json?api-key=(MY KEY: CENSORED)&page=0&q=Trump&fq=document_type:(%22article%22)&begin_date=2019-01-01&end_date=2019-01-05. (Reason: Multiple CORS header ‘Access-Control-Allow-Origin’ not allowed)."
This is where I am getting stuck. I really do not know what is happening here.
The 'Article Search API' is currently using the base URL 'https://nyt-prod.apigee.net/svc/search/v2/articlesearch.json', which I did carefully examine. I do not think the base-url has any problem, as can be conjectured based on the error message I got. I also tried to run this on Chrome and was using the Python testing server, but the problem remains.
I am willing to show the entire html file including the javascript.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>NY Times API example</title>
<link href="nytimes.css" rel="stylesheet">
</head>
<body>
<h1>NY Times video search</h1>
<div class="wrapper">
<div class="controls">
<form>
<p>
<label for="search">Enter a SINGLE search term (required): </label>
<input type="text" id="search" class="search" required>
</p>
<p>
<label for="start-date">Enter a start date (format YYYYMMDD): </label>
<input type="date" id="start-date" class="start-date" pattern="[0-9]{8}">
</p>
<p>
<label for="end-date">Enter an end date (format YYYYMMDD): </label>
<input type="date" id="end-date" class="end-date" pattern="[0-9]{8}">
</p>
<p>
<button class="submit">Submit search</button>
</p>
</form>
</div>
<div class="results">
<nav>
<button class="prev">Previous 10</button>
<button class="next">Next 10</button>
</nav>
<section>
</section>
</div>
</div>
<script>
// Defining a baseURL and key to as part of the request URL
var baseURL = 'https://nyt-prod.apigee.net/svc/search/v2/articlesearch.json';
var key = 'CENSORED';
var url;
// Grab references to all the DOM elements you'll need to manipulate
var searchTerm = document.querySelector('.search');
var startDate = document.querySelector('.start-date');
var endDate = document.querySelector('.end-date');
var searchForm = document.querySelector('form');
var submitBtn = document.querySelector('.submit');
var nextBtn = document.querySelector('.next');
var previousBtn = document.querySelector('.prev');
var section = document.querySelector('section');
var nav = document.querySelector('nav');
// Hide the "Previous"/"Next" navigation to begin with, as we don't need it immediately
nav.style.display = 'none';
// define the initial page number and status of the navigation being displayed
var pageNumber = 0;
var displayNav = false;
// Event listeners to control the functionality
searchForm.addEventListener('submit', submitSearch);
function submitSearch(e) {
pageNumber = 0;
fetchResults(e);
}
function fetchResults(e) {
// Use preventDefault() to stop the form from submitting:
e.preventDefault();
// Assemble the full URL:
url = baseURL + '?api-key=' + key + '&page=' + pageNumber + '&q=' + searchTerm.value + '&fq=document_type:("article")';
if (startDate.value !== '') {
url += '&begin_date=' + startDate.value;
}
if (endDate.value !== '') {
url += '&end_date=' + endDate.value;
}
// Use fetch() to make the request to the API
fetch(url).then(function(result) {
return result.json();
}).then(function(json) {
displayResults(json);
});
}
function displayResults(json) {
while (section.firstChild) {
section.removeChild(section.firstChild);
}
var articles = json.response.docs;
if(articles.length === 10) {
nav.style.display = 'block';
} else {
nav.style.display = 'none';
}
if(articles.length === 0) {
var para = document.createElement('p');
para.textContent = 'No results returned.'
section.appendChild(para);
} else {
for(var i = 0; i < articles.length; i++) {
var article = document.createElement('article');
var heading = document.createElement('h2');
var link = document.createElement('a');
var img = document.createElement('img');
var para1 = document.createElement('p');
var para2 = document.createElement('p');
var clearfix = document.createElement('div');
var current = articles[i];
console.log(current);
link.href = current.web_url;
link.textContent = current.headline.main;
para1.textContent = current.snippet;
para2.textContent = 'Keywords: ';
for(var j = 0; j < current.keywords.length; j++) {
var span = document.createElement('span');
span.textContent += current.keywords[j].value + ' ';
para2.appendChild(span);
}
if(current.multimedia.length > 0) {
img.src = 'http://www.nytimes.com/' + current.multimedia[0].url;
img.alt = current.headline.main;
}
clearfix.setAttribute('class','clearfix');
article.appendChild(heading);
heading.appendChild(link);
article.appendChild(img);
article.appendChild(para1);
article.appendChild(para2);
article.appendChild(clearfix);
section.appendChild(article);
}
}
};
</script>
</body>
</html>
But I think the most important lines are as follows:
var baseURL = 'https://nyt-prod.apigee.net/svc/search/v2/articlesearch.json';
var key = 'CENSORED';
...
...
url = baseURL + '?api-key=' + key + '&page=' + pageNumber + '&q=' + searchTerm.value + '&fq=document_type:("article")';
...
...
fetch(url).then(function(result) {
return result.json();
}).then(function(json) {
displayResults(json);
});
...
...
if(current.multimedia.length > 0) {
img.src = 'http://www.nytimes.com/' + current.multimedia[0].url;
img.alt = current.headline.main;
}
The news articles are supposed to show up on the right hand side in blocks arranged in a vertical fashion. With this error, I saw nothing. I hope someone can help me.