Google apps script, priority between onclick and href - url

I have a button (in HTML) that both calls for a function LoginInfoInput(...) and redirect to a page (href="..."), that is:
<a onclick="LoginInfoInput('<?!= userID; ?>')" href="<?= ScriptApp.getService().getUrl(); ?>?v=form&userID=<?!= userID; ?>" class="waves-effect waves-light btn-large">Log In</a>,
where LoginInfoInput(...) is a javascript function that will trigger my google apps script (GS) function. The triggered GS function will then insert data into my spreadsheet. Meanwhile, the redirected URL will pass though my doGet() function. To determine the outcome, it gets data from my spreadsheet. My problem is that the spreadsheet is not always updated in time before redirecting the page takes action.
My attempt to solve the problem has been to wait/pause with a while-loop. However, when the while-loop is active during which the data becomes available in a spreadsheet (at least visually) does not work, i.e., the interesting data is not accessible. Note, I have also during the while-loop added the option to update the variable that stores the spreadsheet data (calling SpreadsheetApp.openByUrl(url)), however, without any improvement.
Update
My LoginInfoInput() (in JavaScript) is as follows:
function LoginInfoInput(userID){
var userLO = document.getElementById("id_loginLO").value;
var userPassword = document.getElementById("id_password").value;
google.script.run.LoginAdd(userLO, userPassword, userID);
}
Solution
My solution was a combination of the answer provided by #IMTheNachoMan see below and my discovery that the added data in GS to Spreadsheet can be delayed if not forcing the data to be updated. To force an update, one call SpreadsheetApp.flush(); link after inserting the data in Spreadsheet, in the GS function. From my understanding, the data needs to be in my Spreadsheet before I change my (GS)-based page as it will try to access the Spreadsheet at the time my (GS)-based page is called rather than when I call for my Spreadsheet when the data is available (by waiting/using while-loop).

Replace your <a... with this:
<a onclick="return LoginInfoInput(this, '<?!= userID; ?>');" href="<?= ScriptApp.getService().getUrl(); ?>?v=form&userID=<?!= userID; ?>" class="waves-effect waves-light btn-large">Log In</a>
Replace your function with this:
function LoginInfoInput(a, userID)
{
if(a.className == "done")
{
return true;
}
else
{
google.script.run.withSuccessHandler(function(){
a.className = "done";
a.click();
}).LoginAdd();
}
}
When the user clicks the link, className is not done so it calls your GAS function and returns false so the href is not opened. When the GAS function is done, it sets the className and then sets the clicks the link again. This time it returns true so the href would get run.
Does that make sense?
Note, you can also use withUserObject to pass the URL from a around.
Reference: https://developers.google.com/apps-script/guides/html/reference/run

Related

Script function not found doget, but I don't use doGet on my code

I'm doing a PWA using spreadsheet as storage. When I want to submit my info it works well on Web (Chrome, Safari, Opera, etc), it works too like PWA on android.
But when I add the app on Iphone/iPad, when I try to submit the info, it opens a page on safari and show me this error:
Script function not found: doGet.
Google script
// spit out all the keys/values from the form in HTML for email
// uses an array of keys if provided or the object to determine field order
function formatMailBody(obj, order) {
return result; // once the looping is done, `result` will be one long string to put in the email body
}
function doPost(e) {
//operations with values from form
record_data(e);}
function record_data(e) {//operations}
And this is my HTML code:
<form id="gform" class="pure-form pure-form-stacked" data-email="xxxxx#gmail.com" action="https://script.google.com/macros/s/xxxxxxxxxxxxxxxxxxxxx/exec" method="POST">
//Some inputs
<input type="hidden" id="de" name="de" value='nombre'>
<input type="hidden" id="tipopersona" name="tipopersona">
<input type="hidden" id="correo" name="correo" >
</form>
I want to remove the doGet () error and execute the other code as it should or else know what is causing that error just in iOS PWA.
This question is answered in a prior Stack Overflow thread titled: Script function not found: doGet. That answer does not show the code for the solution to the problem, so I'll add that here in case someone else runs across this.
As shared in that answer, all Google Apps Script web application deployments must include a doGet function whether or not the script processes GET requests. I eliminated this error by rewriting the script with the doGet function added, and setting its return value as the doPost function with the event parameter passed to it. Below is a simplified example:
function doPost (e) {
Logger.log(`POST method activated: ${e}`);
return ContentService.createTextOutput(JSON.stringify('doPost is working.'));
}
function doGet (e) {
return doPost(e);
}
The current Google Apps Script documentation for web applications does not seem to mention this requirement, but when doGet is added to a doPost script it will eliminate this particular error.

dojo FilteringSelect from URL default value

Hi I have a working FilteringSelect which reads from a URL. Entering names will query the database and return the appropriate JSON to populate the filtering select, I can select a value and it stores the ID.
<div data-dojo-type="ComboBoxReadStore" data-dojo-id="assignedUserIdstore"
data-dojo-props="url:'Welcome.do?call=JS&actionRefId=142',
requestMethod:'get'"></div>
<input id='assignedUserId' name='value(assignedUserId)'
data-dojo-type='dijit.form.FilteringSelect'
data-dojo-props="store:assignedUserIdstore, pageSize:5, labelAttr:
'label',queryExpr: '*${0}*', autoComplete: false" />
The issue comes with setting the default value. I have this
<script type='text/javascript'>dojo.ready(function(
{dijit.byId('assignedUserId').setValue('25');});
</script>
This appears to work after a fashion - it does call the server and the server returns this
{ "id":"25", "name":"John Smith" "label":"John Smith"}
However it does nothing to actually populate the filtering select with neither a display nor an actual value for the input. I tried to set the value to the name but that had no effect either. Having it return a collection instead of a single item does not help either.
The comboreadstore is defined as
<script type="text/javascript">
require([
"dojo/_base/declare",
"dojox/data/QueryReadStore",
"dojo/parser",
"dijit/form/FilteringSelect"],
function(declare, QueryReadStore){
declare("ComboBoxReadStore", QueryReadStore, {
fetch:function(request) {
// This results in a xhr request to the following URL (in case of GET):
// /url.php?q=<searchString>
request.serverQuery = {q:request.query.name};
return this.inherited("fetch", arguments);
}
});
}
);
</script>
Using dojo.ready doesn't guarantee that the data you're fetching is ready/loaded. It does fire when dojo is ready and all your required assets have been loaded. So i think you're trying to set the FilteringSelect to a value which doesn't exist in the store yet. You could solve this by waiting with setting the value untill the store is ready. How to do that depends on the store you are using which i can't really make up out of your code. I'm not familiar with ComboBoxReadStore. After some googling i found out it might be an extension of dojox.data.QueryReadStore, which is outdated and unfinished. If so i'de suggest you switch to using dojo.store if possible.
Furthermore: The setValue method on dijits is deprecated, you should be using set('key', val).

Caching visited page in Jquery Mobile

I work on Jquery Mobile/Phonegap app for android.
I´d like my app to "remember" that(if) the user has visited one of my pages. For example if he once visits "page1.html", this action should be cached in the phone memory, so that when the user opens the app again there should be possibility to navigate to this "page2.html" directly from "index.thml".
Please, if you have a code suggestion, tell me also how/where do I use it, because sometimes for starters like me it is realy hard to understand what to do with a little piece code.
Thank you very much!
You can use HTML5 local storage for this purpose.
Each time when a page is shown you can save/update the current page URL to a local storage variable, say 'lastVisit', as below:
$(document).on('pageshow', function (event, data) {
var currentPage = $('.ui-page-active').data('url');
localStorage.setItem("lastVisit", currentPage);
});
If you are not getting $('.ui-page-active').data('url'), then you can use $.mobile.activePage.attr('id') which will give you the current page id.
So next time, when the user opens the app again, you can check whether this local storage variable is set and can action accordingly.
The code will look like as below:
$(document).on('mobileinit', function(){
var lastVisit = '';
if(localStorage.getItem("lastVisit") != null){
lastVisit = localStorage.getItem("lastVisit");
}
if(lastVisit){
document.location.href = lastVisit;
}
});
You can use these codes in the header section scripts.

Bookmarklet to save URL in Google Spreadsheet

I want to create a simple bookmarklet, that grabs the URL of the current webpage "location.ref" and saves it in a Google Spreadsheet. After it saves it, I want to stay on the current webpage.
The only way I know of writing to Google Spreadsheet is using Google App Script. So I wrote a simple script that does just that:
function doGet(request) {
var ss = SpreadsheetApp.openByUrl( "https://docs.google.com/spreadsheet/ccc?key=<MY-SPREADSHEET-ID>");
var sheet = ss.getSheets()[0];
var headers = ["Timestamp", "url"];
var nextRow = sheet.getLastRow();
var cell = sheet.getRange('a1');
var col = 0;
for (i in headers){
if (headers[i] == "Timestamp"){
val = new Date();
} else {
val = request.parameter[headers[i]];
}
cell.offset(nextRow, col).setValue(val);
col++;
}
return ContentService.createTextOutput(request.parameter.url)
.setMimeType(ContentService.MimeType.TEXT);
}
I published this as a webapp. I wrote the bookmarklet:
<a href="javascript:(
function(){
alert(window.open('https://script.google.com/macros/s/<MYWEBAPP>/exec?url='+encodeURIComponent(location.href), '_self'));
}
)();">
BOOKMARK
</a>
So far so good. It actually works when I click on the bookmarklet, it does grab the URL of the current webpage and save it in my spreadsheet. But then, the webapp returns a text response and the bookmarklet displays the text causing me to move away from my current website.
Is there a way to ignore the response? GAS webapp script requires me to use doGet() that has to return something. Is there a way to not return anything from GAS script? Alternatively, is there a way i could use some other call to replace window.open to invoke the webapp that would allow me to store the response in a variable and ignore it?
I know it's been over a year but I was trying to do exactly this. It took me a while to figure out, but this works. The 1 second delay was necessary to let the script finish loading.
javascript:(function(){
my_window=window.open('https://script.google.com/macros/s/<MYWEBAPP>/exec?url='+encodeURIComponent(location.href)+'&title='+encodeURIComponent(document.title));
(window.setTimeout(function(){my_window.close();},1000));
void(0);
})();
Instead of using window.open you may consider sending a HTTP GET request using XMLHttpRequest.
Refer here on its usage.
Change _self to something else, e.g. bookmarker and it will open in a new window or tab. If you use it on many pages, they will all reuse the same tab if it keeps the same name.

cucumber , jQuery and redirects

I have a select tag on my page, I have made via jQuery on domready a listener for the change event. In this listener I redirect the user to the same page with a parameter in the url.
When I test the functionality manual, it works very well.
When I run the test, it fails, the redirect doesn't work.
How I can make it work ?
#javascript
Scenario:
When I select "2010" from "year_select"
And I wait 5 seconds
Then "2010" should be selected for "year_select"
Then I should see "time_sheet?year=2010" with html
The test fails, I see time_sheet?year=2011 (the default value) in my html source, manual I see the correct value. It is not updated via Javascript, but according to te year variable passed in the url (after the redirect)
My jQuery code:
jQuery(document).ready(function(){
var yearSelect = jQuery("#year_select");
yearSelect.change(function(){
window.location = "/time_sheet_admin?year="+yearSelect.val();
});
});
What I make wrong ?
I think u have missed out _admin in the scenario. Use time_sheet_admin instead of time_sheet in the scenario. If my suggestions is useless sorry for that.
What I usually do for testing complex JS stuff is to put them in a function and then test if that function was called:
selectChanged = function() {
window.location = "/time_sheet_admin?year="+jQuery("#year_select").val();
}
*note no need for the variable assignment because you only called it once.
then in your feature:
Then I should see "time_sheet?year=2010" in the url
step definition:
When /Then I should see "time_sheet?year=2010" in the url/ do |seconds|
page.execute_script("selectChanged()")
end

Resources