Return Value from function in gs file to javascript file (HTML) - javascript

I need to return value (idValues) from gs file to javascript file.
GS file
function getMachineDB() {
var url = "https://docs.google.com/spreadsheets/d/1ZI16KqM7Uy_ZDTsTUFYFRSXS-DBkRdigp-3SiIy-
CPA/edit#gid=693962232";
var ss = SpreadsheetApp.openByUrl(url);
var sheet = ss.getSheetByName("Equiplist");
var idValues = sheet.getRange(2,4,sheet.getLastRow()).getValues();
return idValues;
}
Javascript file
function regExp(machinenoNum){
google.script.run.withSuccessHandler(function(idValues) {
var vmachineno = "/(" + idValues.join("|") + ")/";
return (vmachineno.test(machinenoNum));
}).getMachineDB();
}
function isValid() {
var x = document.getElementById("machineno");
if (regExp(x.value)) {
return true
} else {
machineno.setCustomValidity('nomor mesin tidak terdaftar');
return false
}
}
HTML file
<input type="text" id="machineno" class="form-input" name="entry.1002184227" placeholder="xx-xxx-xx" onChange="return isValid()" oninput="this.setCustomValidity('')" required/>
These code is comparing value from text box form with some data from Spreadsheet. How to get value of idValue from GS file to javascript file? thank you

Current behavior
To reproduce your behavior, I created a new project to test. I also used the values 1-9 in the spreadsheet for validation.
When you run the web app currently, it displays this in the console:
This relates to this code:
function regExp(machinenoNum){
google.script.run.withSuccessHandler(function(idValues) {
var vmachineno = "/(" + idValues.join("|") + ")/";
return (vmachineno.test(machinenoNum));
}).getMachineDB();
}
If you add some console.log to this code:
function regExp(machinenoNum){
google.script.run.withSuccessHandler(function(idValues) {
console.log(idValues)
var vmachineno = "/(" + idValues.join("|") + ")/";
console.log(vmachineno)
return (vmachineno.test(machinenoNum));
}).getMachineDB();
}
You get:
idValues is [[1],[2],[3],[4],[5],[6],[7],[8],[9]]
vmachineno is "/(1|2|3|4|5|6|7|8|9|)/"
vmachineno is just a string, it does not have any method called test on it. So this is why you get an error here.
There is also no action attached to not finding the value in the Spreadsheet, so even if the error was resolved, this would still not warn the user about invalid input.
Solution
function regExp(machinenoNum){
google.script.run.withSuccessHandler(function(idValues) {
// This just converts the `idValues` into simple array of strings.
const values = idValues.map(val => `${val[0]}`)
// Checking if the values from SS contain the machinenoNum value
// If this is true, function returns and does nothing.
if (values.includes(machinenoNum)) return true
// If the above check is false, then this will make an alert in the browser window.
alert("invalid input!")
})
.getMachineDB();
}
So when you enter a value that is in the spreadsheet, nothing will happen. If the value is not in the spreadsheet, then you get this:
EDIT - Walkthrough of code:
The page is loaded and you have an empty text box
The user inserts a value, for example 11 and presses ENTER.
This causes onChange="return isValid()" from the HTML to run.
isValid() will get the value from the text box with var x = document.getElementById("machineno");
Then will use this value here: if (regExp(x.value)) {
So now regExp is running.
This will now get the values from the spreadsheet and test if the x.value is contained there.
If it is not, then it will produce an alert:

Related

How can I pass a server-side variable to HTML and have the HTML page return an auto-filled form?

I am building an HTML page in Google Apps Script with CRUD functionality. The user can currently add data, edit data, and search data (I am not adding a delete feature). I would like the user to receive the form url link with an ID that when they go BACK to that link, it auto-fills the form with the previously added data.
In my HTML file, I have the following button defined:
document.getElementById("sbtn").addEventListener("click",getTID);
Once a user has entered data, it gets sent to a Google Sheet. The user HAS to enter a unique ID that they've already been provided. Using this ID, they can enter it, hit search, and it runs getTID():
function getTID() { //TID CODE
var transID = document.getElementById("tid").value;
if (transID.length === 36) {
google.script.run.withSuccessHandler(updateAllData).getID(transID);
} else {
alert("Transaction ID is not long enough.\nPlease copy the Transaction ID EXACTLY!\n\nFor Example: https:/workwebsiteconcealedforprivacy/w?txid=36275284-2ed6-4868-97b2-16bc1fde1a08\n\nThe Transaction ID is: 36275284-2ed6-4868-97b2-16bc1fde1a08")
}
}
This takes the ID they gave, references the spreadsheet and then returns values it found by index. Now, I have in my server-side GS file, the following in doGet:
var urlValue = '';
function doGet(e) {
// Test Code
var ss = SpreadsheetApp.openById(id);
var ws = ss.getSheetByName("Options");
var list = ws.getRange(1, 1, ws.getRange("A1").getDataRegion().getLastRow(), 1).getValues();
var htmlListArray = list.map(function (r) { return '<option>' + r[0] + '</option>'; }).join('');
var title = "Please Work";
var vals = JSON.stringify(e);
if ('v' in e.parameter){
urlValue = String(e.parameter['v']);
//return HtmlService.createHtmlOutput(urlValue);
}
return render("page",{list: htmlListArray, title});
and the following:
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
function render(file, argsObject) {
var tmp = HtmlService.createTemplateFromFile(file);
if (argsObject) {
var keys = Object.keys(argsObject);
keys.forEach(function (key) {
tmp[key] = argsObject[key]
});
}
return tmp.evaluate();
}
If I uncomment the return HtmlService.createHtmlOutput(urlValue); line, I can see that IF an ID is in the URL, it returns the correct parameter.
My problem is that I cannot get the HTML to read the urlValue variable and autorun getTID() when the user enters the url with the correct parameter. The correct functionality is that IF the parameter is found, it auto populates the HTML form. If it doesn't, it returns the blank form.
There is an error on
return render("page",{list: htmlListArray, title});
On {list: htmlListArray, title} the name of the second property is missing.
To "read the urlValue variable" there are two options:
pass the parameters from the URL using the event object of the doGet function. For this you have two options, create the HtmlService.HtmlOutput object from an html string generated using "vanilla" JavaScript or create it from a HtmlService.HtmlTemplate object.
get the parameters from the URL directly on the client-side code using google.script.url.getLocation .
If you go for the first option, then you should pass someway the urlValue to the render function. In the question code urlValue is a global variable, so you might add the following before the render's return statement.
tmp.urlValue = urlValue;
Then you have to add a scriptlet on the html file to handle this value and "autorun" getTID. Scriptlets is a feature of Templated HTML.

Take selected text, send it over to Scryfall API, then take the link and put it in the selected text

I've been able to sort out the middle bit (the API seems to be called to just fine) along with the submenu displaying. Originally I thought that just the end part wasn't working but I'm now thinking that the selection part isn't either.
What am I doing wrong with the getSelection() and what do I need to do to insert a link into said selection? (to clarify, not to replace the text with a link, but to insert a link into the text)
//Open trigger to get menu
function onOpen(e) {
DocumentApp.getUi().createAddonMenu()
.addItem('Scry', 'serumVisions')
.addToUi();
}
//Installation trigger
function onInstall(e) {
onOpen(e);
}
//I'm not sure if I need to do this but in case; declare var elements first
var elements
// Get selected text (not working)
function getSelectedText() {
const selection = DocumentApp.getActiveDocument().getSelection();
if (selection) {
var elements = selection.getRangeElements();
Logger.log(elements);
} else {
var elements = "Lack of selection"
Logger.log("Lack of selection");
}
}
//Test run
// insert here
// Search Function
function searchFunction(nameTag) {
// API call + inserted Value
let URL = "https://api.scryfall.com/cards/named?exact=" + nameTag;
// Grabbing response
let response = UrlFetchApp.fetch(URL, {muteHttpExceptions: true});
let json = response.getContentText();
// Translation
let data = JSON.parse(json);
// Jackpot
let link = data.scryfall_uri;
// Output
Logger.log(link);
}
// Test run
searchFunction("Lightning Bolt");
//Let's hope this works how I think it works
function serumVisions() {
const hostText = getSelectedText();
const linkage = searchFunction(hostText);
// Unsure what class I'm supposed to use, this doesn't
const insertLink = DocumentApp.getActiveDocument().getSelection().newRichTextValue()
.setLinkUrl(linkage);
Logger.log(linkage);
}
For the first part, I tried the getSelection() and getCursor() examples from the Google documentation but they don't seem to work, they all just keep returning null.
For the inserting link bit, I read all those classes from the Spreadsheet section of the documentation, at the time I was unaware but now knowing, I haven't been able to find a version of the same task for Google Docs. Maybe it works but I'm writing it wrong as well, idk.
Modification points:
In your script, the functions of getSelectedText() and searchFunction(nameTag) return no values. I think that this might be the reason for your current issue of they all just keep returning null..
elements of var elements = selection.getRangeElements(); is not text data.
DocumentApp.getActiveDocument().getSelection() has no method of newRichTextValue().
In the case of searchFunction("Lightning Bolt");, when the script is run, this function is always run. Please be careful about this.
When these points are reflected in your script, how about the following modification?
Modified script:
Please remove searchFunction("Lightning Bolt");. And, in this case, var elements is not used. Please be careful about this.
From your script, I guessed that in your situation, you might have wanted to run serumVisions(). And also, I thought that you might have wanted to run the individual function. So, I modified your script as follows.
function getSelectedText() {
const selection = DocumentApp.getActiveDocument().getSelection();
var text = "";
if (selection) {
text = selection.getRangeElements()[0].getElement().asText().getText().trim();
Logger.log(text);
} else {
text = "Lack of selection"
Logger.log("Lack of selection");
}
return text;
}
function searchFunction(nameTag) {
let URL = "https://api.scryfall.com/cards/named?exact=" + encodeURIComponent(nameTag);
let response = UrlFetchApp.fetch(URL, { muteHttpExceptions: true });
let json = response.getContentText();
let data = JSON.parse(json);
let link = data.scryfall_uri;
Logger.log(link);
return link;
}
// Please run this function.
function serumVisions() {
const hostText = getSelectedText();
const linkage = searchFunction(hostText);
if (linkage) {
Logger.log(linkage);
DocumentApp.getActiveDocument().getSelection().getRangeElements()[0].getElement().asText().editAsText().setLinkUrl(linkage);
}
}
When you select the text of "Lightning Bolt" in the Google Document and run the function serumVisions(), the text of Lightning Bolt is retrieved, and the URL like https://scryfall.com/card/2x2/117/lightning-bolt?utm_source=api is retrieved. And, this link is set to the selected text of "Lightning Bolt".
Reference:
getSelection()

Stuck trying to reference value from text input in JavaScript function

I am trying to create a plugin for Photoshop. My file structure is as follows (as recommended in Adobe's guidelines on building plugins):
My root folder contains two folders: client and host.
The client folder contains index.html and index.js.
Index.html provides the design and references both javascript docs.
Index.js contains references to the elements in index.html, and sends directions to trigger functions contained in the index.jsx folder mentioned below.
The host folder contains index.jsx, which contains all the functions that actually trigger photoshop's tools. To get this javascript code, I've used xtools on mac to convert the photoshop actions I've created into .jsx files.
I want my plugin to have a text box in which users can input a value (it has to be a number), and then hit a button to trigger a function, a part of which I need to contain the value of the number they entered.
In this example, the function will resize the image to the user's desired number of pixels.
(Note: - the script works perfectly when the number is static/preset within the function and the button simply triggers the function without any other input - I'm just struggling with getting the value from the text box into the function).
Here's the code on each document:
index.html:
<input type="text" id="resizeinput" placeholder="enter value in pixels">
<button id="resizebutton">Go!</button>
Index.js:
var customresize = document.querySelector("#resizebutton");
customresize.onclick = function() {
csInterface.evalScript("ResizeCustom()");
};
Index.jsx:
cTID = function(s) { return app.charIDToTypeID(s); };
sTID = function(s) { return app.stringIDToTypeID(s); };
//
//==================== Resize Custom ==============
//
function ResizeCustom() {
// Image Size
function step1(enabled, withDialog) {
if (enabled != undefined && !enabled)
return;
var dialogMode = (withDialog ? DialogModes.ALL : DialogModes.NO);
var desc1 = new ActionDescriptor();
desc1.putUnitDouble(cTID('Wdth'), cTID('#Pxl'), 1234);
desc1.putBoolean(sTID("scaleStyles"), true);
desc1.putBoolean(cTID('CnsP'), true);
desc1.putEnumerated(cTID('Intr'), cTID('Intp'), sTID("bicubicSmoother"));
executeAction(sTID('imageSize'), desc1, dialogMode);
};
step1(); // Image Size
};
Where the JavaScript in index.jsx says "1234" is where I need to reference the value entered in the text box "#resizeinput".
My best bet so far was to replace 1234 with:
document.getElementById("#resizeinput").value)
But it didn't work.
In your Index.js:
var customresize = document.querySelector("#resizebutton"),
resizeInput = document.querySelector("#resizeinput");
customresize.onclick = function()
{
csInterface.evalScript("ResizeCustom(" + resizeInput.value + ")");
};
in Index.jsx:
cTID = function(s) { return app.charIDToTypeID(s); };
sTID = function(s) { return app.stringIDToTypeID(s); };
function ResizeCustom(_arg) {
var desc1 = new ActionDescriptor();
desc1.putUnitDouble(cTID('Wdth'), cTID('#Pxl'), _arg);
desc1.putBoolean(sTID("scaleStyles"), true);
desc1.putBoolean(cTID('CnsP'), true);
desc1.putEnumerated(cTID('Intr'), cTID('Intp'), sTID("bicubicSmoother"));
executeAction(sTID('imageSize'), desc1, DialogModes.NO);
};
Note that this doesn't do any checks (if you'll try to pass a string, there'll be an error).
p.s. in the world of Abdobe plugins are written in C++, and HTML panels are extensions. Just in Case :)
p.p.s I'd recommend checking Davide Barranca's blog with HTML Panels Tips, for example on passing data from JS to JSX: HTML Panels Tips: #4 passing Objects from HTML to JSX

return new Model result from JS to xml in odoo

I'm trying to get value from python function and assign that value to xml coulmn,
JS function called in xml :
var Users = new openerp.web.Model('res.users');
var im = new openerp.im_chat.InstantMessaging(self);
Users.call('has_group', ['base.group_user']).done(function(is_employee) {
alert(is_employee);
return is_employee;
});
How can i return the "is_employee" value to xml field ? Now it always shows undefined in field, but alert prints value. Please anyone help me

getting responseID of latest form submission in google apps

I have a google form. Every time it is submitted, the answers go into a google spreadsheet. I am trying to write an app script that triggers every time the form is submitted, and adds an "edit" link in the column to the right of the data from the form. The link itself is easy to generate, google has a method called getEditResponseURL(). (https://developers.google.com/apps-script/reference/forms/form-response)
But everytime I run it, I am getting the error "TypeError: Cannot call method "getResponses" of null."
Here is my code:
function addeditlink(e) {
// Get the active sheet
var sheet = SpreadsheetApp.getActiveSheet();
// Get the active row
var row = sheet.getActiveCell().getRowIndex();
//get the form
var form = FormApp.getActiveForm();
//get latest form response
var responses = form.getResponses();
var lastResponse = responses[responses.length - 1];
//get edit URL
var editurl = lastResponse.getEditResponseUrl();
//build link
var editlink = "Edit";
//place edit link in column R (index 18)
if (sheet.getRange(row, 18).getValue() == "") {
sheet.getRange(row, 18).setValue(editlink);
}
}
Any help? Thanks!
The solution is to remove:
var form = FormApp.getActiveForm(); //this is WRONG
and replace with:
var form = FormApp.openByID(' ID here ')
There is no "active form", because this script is being run in sheets, not forms.
I think you can only call FormApp.getActiveForm() from a script attached to a form, whereas your script is contained in a GSheet. I couldn't find a way to easily gets forms that used this sheet as its destination so what I've did was get all of the forms and then looked at the destination id of each and checked if it is the same as this spreadsheet. Once you've got your Form object you can get the responses. Feels a bit long winded would love to know if anyone knows a quicker way.
There are also a few exceptions that FormApp throws that you have to cope with.
Here's the function I use:
/**
* Find the first form that is linked to a specific spreadsheet
*
* #param {string} spreadsheet id
* #return {object} Form or null
*/
function getFormByDestinationId_(spreadsheetId) {
var formFiles = DriveApp.getFilesByType('application/vnd.google-apps.form');
var form;
var formFile;
var formId;
var destinationId;
while (formFiles.hasNext()) {
formFile = formFiles.next();
formId = formFile.getId();
// Throws an error if ID invalid
try {
form = FormApp.openById(formId);
} catch (error) {
if (error.name === "Exception") {
// Just ignore it
} else {
throw error;
}
}
// Form.getDestinationId() throws an error if there is no destination id
try {
destinationId = form.getDestinationId();
} catch (error) {
if (error.name === "Exception") {
// Just ignore it
} else {
throw error;
}
}
if (destinationId !== spreadsheetId) {
continue;
}
return form;
}
return null;
} // getFormByDestinationId_()
The only line using: getResponses() method is this one:
var responses = form.getResponses();
Your error:
Cannot call method "getResponses" of null
Means that form is null. If form is null, then this line:
//get the form
var form = FormApp.getActiveForm();
is not working. So, why isn't it working? There is nothing wrong with the code, so it must be a different problem. If there was an active form, that code would return a form type. This means that there is no form bound to the script. getActiveForm()
Returns the form to which the script is container-bound.
Your script is not "container-bound" to the form. Your script is bound to the spreadsheet.
The documentation states:
To interact with forms to which the script is not container-bound, use openById(id) or openByUrl(url) instead.
You can bind your script to the form by opening the script editor from the edit page of the form. But, there's no need to do that if you want to keep your script bound to the spreadsheet.
The line var form = FormApp.getActiveForm(); isn't going to work in your spreadsheet script.
The problem with using the Event Object e with an installable trigger, is that it looks like you can't get the response URL.
google_sheets_events
This means that you need to use openById(id) or openByUrl(url) inside the script bound to the spreadsheet, or move all your script to the form.
Here is how to get the edit url from script in the spreadsheet:
// Open a form by ID.
var form = FormApp.openById('1234567890abcdefghijklmnopqrstuvwxyz');
Now the problem is, that you can only get the Edit Response URL: getEditResponseUrl() through the "FormResponse" class. So you need the Form Responses.
var formResponses = form.getResponses();
But that's all the responses, you need the last one.
var lastResponseIndex = formResponses.length - 1;
var lastResponse = formResponses[lastResponseIndex];
var editURL = lastResponse.getEditResponseUrl();
or:
function getEditURLofLastResponse() {
// Open a form by ID.
var form = FormApp.openById('Your Form ID');
var formResponses = form.getResponses();
//get last respnse
var lastResponseIndex = formResponses.length - 1;
var lastResponse = formResponses[lastResponseIndex];
var editURL = lastResponse.getEditResponseUrl();
Logger.log(editURL);
}
Just an observation:
You are using an e argument: function addeditlink(e) {. But I don't see it being used in your code. That makes me wonder if you are using an "installable" trigger, as opposed to a "simple" trigger.
It's possible to get the values that were just submitted with e.values or e.namedValues. But you can't get the Edit URL with the Event Object.

Categories