I’m trying to create a simple lookup form using Google Apps Script. The form uses data sourced from a spreadsheet to populate a number of autocomplete input fields. I can’t figure out why the autocomplete is not working. In supplierIDLookupFormHTML.html I put the script tag for the materialize js file at the very end of the body tag.
The function showSidebar_sellerID()
in show_sidebars.js does append a simple div
tag to the end of the body tag. But that shouldn’t affect the materialze js code, should it?
Here is my code.
supplierIDLookupFormHTML.html
<!DOCTYPE html>
<html lang="en">
<head>
<base target="_top">
<!-- Required meta tags -->
<meta charset="utf-8">
<!--Let browser know website is optimized for mobile-->
<meta id="viewport" content="width=device-width, initial-scale=1.0"/>
<!--Import Google Icon Font-->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!--Import materialize.css-->
<link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css" media="screen,projection"/>
<!-- Add the standard Google Style Sheet. -->
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
<!--Import supplierIDLookupFormHTML,css -->
<?!= HtmlService.createHtmlOutputFromFile('supplierIDLookupFormHTML.css').getContent(); ?>
</head>
<body>
<div class="container">
<!-- <h1>New Customer</h1> -->
<form id="new-customer-form" onsubmit="onSubmitFormHandler(event)">
<div class="row">
<div class="input-field col m6">
<label for="companyName">Company Name</label>
<input type="text" id="companyName" name="companyName" class="autocomplete">
</div>
</div><!-- end .row -->
<div class="row">
<div class="input-field col m6">
<label for="ebayID">ebayID</label>
<input type="text" id="ebayID" name="ebayID" class="autocomplete">
</div>
</div><!-- end .row -->
<div class="row">
<div class="input-field col m6">
<label for="amazonSellerID">Amazon Seller ID</label>
<input type="text" id="amazonSellerID" name="amazonSellerID" class="autocomplete">
</div>
</div><!-- end .row -->
<div class="row">
<div class="input-field col m6">
<label for="aliexpressSellerID">Aliexpress Seller ID</label>
<input type="text" id="aliexpressSellerID" name="aliexpressSellerID" class="autocomplete">
</div>
</div><!-- end .row -->
<div class="row">
<div class="input-field col m6">
<label for="supplierID">Seller ID</label>
<input type="text" id="supplierID" name="supplierID" readonly="readonly">
</div>
</div><!-- end .row -->
<div class="row">
<div class="input-field col m6">
<label for="selectedSupplierName">Selected Supplier Name</label>
<input type="text" id="selectedSupplierName" name="selectedSupplierName" readonly="readonly">
</div>
</div><!-- end .row -->
<div class="row">
<div class="input-field col m6 p-1">
<button type="submit" class="btn waves-effect waves-light" name="submitBtn" id="submitBtn" value="submit">Submit
<i class="material-icons right">Submit</i>
</button>
</div>
<div class="input-field col md6">
<button type="close" class="btn btn-secondary btn-block" id="closeBtn" value="Close" onclick="google.script.host.close()">Close</button>
</div>
</div><!-- end .row -->
</form><!-- end form -->
<p>
<div id="errorMsg"></div>
</p>
<p>
<div id="sucessMsg"></div>
</p>
</div><!-- end .container -->
<!--JavaScript at end of body for optimized loading-->
<!-- Initialise: jQuery -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script>
<!-- Initialise: LoDash-->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
<!-- Shared javascript functions -->
<?!= HtmlService.createHtmlOutputFromFile('sharedJavascriptFunctionsJS').getContent(); ?>
<?!= HtmlService.createHtmlOutputFromFile('supplierIDLookupFormHTML.js').getContent(); ?>
<!-- Compiled and minified JavaScript -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
</body>
</html>
src/supplierIDLookupFormHTML.js.html
<script>
// 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();
});
}
}
document.addEventListener('DOMContentLoaded', function() {
window.addEventListener('load', preventFormSubmit);
//google.script.run.withSuccessHandler(populateSuppliers).getSuppliers();
initialize();
google.script.run.withSuccessHandler(populateCompanyNames).getSupplierCompanyNames();
/*google.script.run.withSuccessHandler(populateEbayIDs).getSupplierebayIDs();
google.script.run.withSuccessHandler(populateAmazonSellerIDs).getSupplierAmazonSellerIDs();
google.script.run.withSuccessHandler(populateAliexpressSellerIDs).getSupplierAliexpressSellerIDs();*/
});
// REWRITE
function buildAutoCompleteDataObject() {
var customers = getDataFromHtml('customerObj_htmlservice');
const customer = customers[Object.keys(customers)[0]];
console.log(customer);
//console.log(customer.customerID);
//console.log(customer.firstName);
// fill in the form fields using the data from the customer object.
// loop through each entry of the customer object and match the entry with an element in the current document.
Object.entries(customer).forEach((entry) => {
const [key, value] = entry;
//console.log(`${key}: ${value}`);
//console.log(typeof `${value}`);
var element = document.getElementById(`${key}`);
if (element) {
console.log("element (" + `${key}` + ") exists");
document.getElementById(`${key}`).value = `${value}`;
} else {
console.log("Element (" + `${key}` + ") does not exist")
}
});
}
function getDataFromHtml(idData) {
if (!idData)
idData = "mydata_htmlservice";
var dataEncoded = document.getElementById(idData).innerHTML;
//console.log(dataEncoded);
var data = JSON.parse(dataEncoded);
return data;
}
//FIX THIS
function initialize() {
const suppliers = getDataFromHtml('suppliersObj_htmlservice');
const supplier = suppliers[Object.keys(suppliers)[0]];
//console.log("const suppliers: " + suppliers);
//console.log("const supplier: " + supplier);
_.forEach(suppliers, function(supplier) {
_.forEach(supplier, function(value, key) {
//console.log("lodash nested forEach():")
//console.log(`${key}: ${value}`);
});
console.log("");
});
//console.log(suppliers[0][supplierid]);
}
function populateCompanyNames(companyNames){
console.log("running: populateCompanyNames()");
console.log(companyNames);
var autocomplete = document.getElementById('companyName');
var instances = M.autocomplete.init(autocomplete, { data: companyNames });
}
function populateEbayIDs(ebayIDs){
let autocomplete = document.getElementById('ebayID');
let instances = M.autocomplete.init(autocomplete, {data: ebayIDs});
}
function populateAmazonSellerIDs(amazonSellerIDss){
let autocomplete = document.getElementById('amazonSellerID');
let instances = M.autocomplete.init(autocomplete, {data: amazonSellerIDs});
}
function populateAliexpressSellerIDs(aliexpressSellerID){
let autocomplete = document.getElementById('aliexpressSellerID');
let instances = M.autocomplete.init(autocomplete, {data: aliexpressSellerIDs});
}
function onListFailure(error) {
//alert(error.message);
console.log("onListFailure() triggered. Error message was: " + error.message);
}
//const dropdownIDs = ["companyName", "ebayID", "amazonSellerID", "aliexpressSellerID"];
function handleFormSubmit(formObject) {
google.script.run.processForm(formObject);
document.getElementById("myForm").reset();
}
function onClickSubmitBtnHander() {
var supplierID = $("#supplierID").val();
google.script.run.setCurrentCellSupplierID(supplierID);
}
</script>
show_sidebars.js
/**
* @function showSidebar_sellerID
* @description TODO
*/
function showSidebar_sellerID() {
var SIDEBAR_TITLE = 'SellerID Lookup';
var suppliersObj = JSON.stringify(getSuppliers());
//console.log("suppliersObj: " + suppliersObj)
const sidebar = HtmlService.createTemplateFromFile('supplierIDLookupFormHTML')
var htmlOutput = sidebar.evaluate();
var strAppend = "<div id='suppliersObj_htmlservice' style='display:none;'>" + suppliersObj + "</div>";
htmlOutput.append(strAppend);
htmlOutput.setTitle(SIDEBAR_TITLE);
SpreadsheetApp.getUi().showSidebar(htmlOutput);
}
Error shown in developer console
Uncaught
TypeError: Cannot read properties of undefined (reading 'init')
at populateCompanyNames (userCodeAppPanel:81:36)
at Ph (695454968-mae_html_user_bin_i18n_mae_html_user__en_gb.js:149:334)
at 695454968-mae_html_user_bin_i18n_mae_html_user__en_gb.js:36:276
at mf.N (695454968-mae_html_user_bin_i18n_mae_html_user__en_gb.js:102:380)
at Ed (695454968-mae_html_user_bin_i18n_mae_html_user__en_gb.js:64:477)
at a (695454968-mae_html_user_bin_i18n_mae_html_user__en_gb.js:62:52)
2
Answers
Since the HTML template includes a closing HTML tag, the
showSidebar_sellerID
function should not useHtmlService.HtmlOutput.append
to append code, as this will cause the appended code to be added after the closing HTML tag.P.D. There might be other problems, but the question doesn’t include an "MCVE". The code from the file
sharedJavascriptFunctionsJS
is missing. Because it’s missing, it is unclear if it is a .gs or a .html file. If it’s a .gs file another problem is adding it usingHtmlService.createHtmlOutputFromFile
as this method requires a .html file.Autocomplete MaterializeCSS is not working
When I saw your showing script, you are using as follows.
I think that in this case,
a
ofautocomplete
is required to beA
which is the capital letter. Ref So, how about the following modification? Please modify your allM.autocomplete.init
toM.Autocomplete.init
.When I tested your script by using a sample value of
companyNames
, I confirmed the same errorTypeError: Cannot read properties of undefined (reading 'init')
atvar instances = M.autocomplete.init(autocomplete, { data: companyNames });
. And, when I modified it tovar instances = M.Autocomplete.init(autocomplete, { data: companyNames });
, I confirmed that the script worked.Note:
TypeError: Cannot read properties of undefined (reading 'init')
. This modification supposes that the other parts of your script work fine. Please be careful about this.data
forM.Autocomplete.init(autocomplete, {data: ###})
. So, if your value ofdata
is invalid, please modify it.Reference: