Problems in using global variables in functions - google-apps-script

I try to use a global variable in my function, see below. I can use the global variable in my func1 to store a value, for Ex. 77, ok. In func2 I try to use the current value that is stored in the global variable but the result is undefined.
Any advice how to get this working?
doGet() {
...
var buttonValue;
...
func1(e,buttonValue) {
...
buttonValue = size;
throw buttonValue;
--> buttonValue ok!
...
}
func2(e,buttonValue) {
...
throw buttonValue;
--> buttonValue undefined!
}

You cannot do that. The value of global variables cannot be changed in handler functions etc.
Use CacheService to store values that have a global scope
Here is a code example:
function func1(){
CacheService.getPrivateCache().put('var_name', 'var_value');
}
function func2(){
var var_value = CacheService.getPrivateCache().get('var_name');
}

Related

How to use GAS classes in different files, independent of file load order?

Consider a following simple example, file country.gs
class Country { }
and file file subcountry.gs
class SubCountry extends Country{ }
function test(){}
Trying to run test() I get
ReferenceError: Country is not defined
If I join files or change loading order, it works fine.
Apparently, I don't want to be dependent on file load order, also clasp changes on push(sorting alphabetically), so it's definitely not a good way to rename files in order they should be compiled.
Is there an appropriate solution for this?
Example:
https://script.google.com/d/1Pipt3YN1FBGkbRRT2PyCHhugd-Xrv3zctIWYwX-cGnAjXfDckwOk7bJh/edit?usp=sharing
As written in the documentation,
This arrangement is identical to how browsers handle multiple tags in one HTML file.
Each file is like a new <script>file content </script> tag and they're added in the order they appear in Apps script editor. This is a problem only when you're using global variables. It's explicitly discouraged to use global variables.
Caution: It's not best practice to rely on a specific file parse order to avoid this issue. The sequence of script file parsing can change if script files are copied, removed, renamed, or otherwise rearranged. It's better to remove any global variable dependency on function calls if possible.
Classes are infact "special functions". You can always enclose the Class in a local scope and call, when needed as recommended in the documentation.
Snippet:
Just moving the calling function to local scope should work
/*subcountry.gs*/
function test(){
/*local scope*/class SubCountry extends Country{ }
}
To avoid declaring class in global scope as well:
/*country.gs*/
var Country;
function main(){
if (Country == undefined) Country = class Country { }
return Country;
}
/*subcountry.gs*/
function test(){
/*initialize class Country*/main()
/*local scope*/class SubCountry extends Country{ }
}
Building off the answer posted by TheMaster and the Bruce Mcpherson article shared by Alan Wells, you could try implementing your own require() function.
/* Code.gs */
function test() {
const SubCountry = require("SubCountry");
const x = new SubCountry();
}
/* SubCountry.gs */
function SubCountry() {
const Country = require("Country");
return class SubCountry extends Country {};
}
/* Country.gs */
function Country() {
return class Country {};
}
/* Require.gs */
function require(moduleName) {
const modules = {
Country: Country,
SubCountry: SubCountry,
};
return modules[moduleName]();
}
Alternatively, you could apply a more direct approach without the use of require(), but I find this to be slightly less intuitive.
/* Code.gs */
function test() {
const x = new (SubCountryClass())();
}
/* SubCountry.gs */
function SubCountryClass() {
return class SubCountry extends CountryClass() {};
}
/* Country.gs */
function CountryClass() {
return class Country {};
}
All files above, for both approaches, are intentionally presented and loaded in an order that would cause a ReferenceError if declaring the classes globally. So this should be fully independent of load order.
I'll probably go with one of solutions described here
TypeScript classes order in Google AppScript Project
using clasp and it's filePushOrder option
{
"scriptId":"1Pipt3YN1FBGkbRRT2PyCHhugd-Xrv3zctIWYwX-cGnAjXfDckwOk7bJh",
"filePushOrder": [
"country.gs",
"subcountry.gs"
]
}
Author example
https://github.com/PopGoesTheWza/clasp-filePushOrder
I enforces me to use clasp, but at least it's easy to maintain.

pass a local variable to an click function

I want to pass a local variable from one function to another and I have tried some solutions but they didn't work because I have a click function, I need to put the variable first of all and I don't how to do it, also I declared the variable outside the function but if I use it outside of all the functions it doesn't has all its values or inside the function resaltar nothing appears, any help is welcome
let children=$('div[class^="fila"], div[class^="pieceRow"]' ).children()
var clase
$(children).each(function getClass(){
clase=$(this).attr('class')
$(clase).on('click', function resaltar(){
if (clase==clase) {
$(this).addClass('shadow')
}
})
})
this is the html code https://jsfiddle.net/qb5fwcus/
Please try this code :
let children = $('div[class^="fila"], div[class^="pieceRow"]' ).children();
$(children).on('click', function(){
var clase = $(this).attr('class');
resaltar(clase);
})
function resaltar(clase){
$('.shadow').removeClass('shadow');
$('.' + clase).addClass('shadow');
}
Explanation : You can not pass any value for the callback function for any event handler. Either it can be an anonymous function, or a function, not requiring any argument. However, you can achieve that, by making the callback function anonymous, and call any function from it. In this way, you can pass variables.
PS : Let me know if I got it wrong in any manner :)
Let's assume that you will be passing it to a pure JS function.
function myFunc() {
console.log("My function!");
}
In your 'click', you're calling the function ''resalter'', that you're also defining on the spot.
You want to call myFunc, so:
$(clase).on('click', myFunc())
Now, myFunc is not expecting a variable. Let's just pass a variable:
function myFunc(myVar) {
console.log("Passing a variable of type: " + typeof myVar);
}
Now, you're only expected to pass this var in the function you're calling. Given the previous example I gave, we have:
let x = 1; // our variable
$(clase).on('click', myFunc(x))
This way you're passing 'x' as a variable, of type integer. Use this code as inspiration to try and reach your goal. It is a bit hard to give a more exact answer, given that we don't know what variables have to be passed to what function and what the purpose is.
Good luck!

Cannot update global variable within addeventListener function

Basically when running addEventListener I cannot access any of my saved variables from outside the function I am creating.
In the following code I always get the error Property 'xAxisLabel' does not exist on type 'HTMLElement'.
xAxisLabel:string = 'xAxis';
xAxisField:HTMLElement;
filterChanged(element: HTMLElement) {
element.addEventListener("change", function(){
this.xAxisLabel = 'Countries';
});
}
ngOnInit() {
this.xAxisField=document.getElementById('xAxisField');
this.filterChanged(this.xAxisField);
}
I am sure it's a fairly simple solution but I haven't been able to find it online. Any help would be appreciated.
It is due to 'this' keyword binding. You need to change your code to use arrow function, so:
filterChanged(element: HTMLElement) {
element.addEventListener("change", () => {
this.xAxisLabel = 'Countries';
});
}
now this.xAxisLabel refers to correct value

How to call a global var document.getElementById into a function to get the .className

That's what I use to toggle between the Login and Register divs.
The following is actually working:
function myNavLogin()
{
document.getElementById("Content-Login").className = "Content-On";
document.getElementById("Content-Register").className = "Content-Off";
}
I want to call the next global variable:
var divLogin = document.getElementById("Content-Login");
var divRegister = document.getElementById("Content-Register");
Inside the function like this way:
function myNavLogin()
{
divLogin.className = "Content-On";
divRegister.className = "Content-Off";
}
The second example isn't working, I think because the variable isn't declared by the correct way or I am calling it bad... please help, thanks.
Regards, Chicler ;)
Your question is probably a simple variable scoping one, but here's a more thorough response anyway. Global variables are a horrible way to code. You should try to use a more object oriented approach. You should also consider keeping tag names out of your variable names, since markup is apt to change. Finally, when toggling classes, it's better to simply add and remove an "on" state and have the "off" state be the default. Here's an example:
var AccountLogic = function(){
this.elLogin = document.getElementById("Content-Login");
this.elRegister = document.getElementById("Content-Register");
this.login = function(){
this.elLogin.classList.add("content-on");
this.elRegister.classList.remove("content-on");}
}
}
var accountLogic = new AccountLogic();

Is there a way to capture the id of the dom element in a variable?

I have an element in "this" how do i get its id's value (and class's value)?
alert(this.id) ;
returns undefined.
First of all you should check if this is really your target element and not global window object. Let me illustrate my advice:
function foo() {
if (this === window) {
alert("'this' is actually 'window'");
} else {
alert("'this' is not 'window'");
}
}
foo(); // will alert: 'this' is actually 'window'
but:
document.onclick = foo;
// every mouse click will produce alert: 'this' is not 'window'
Anyway, I'd suggest you to use Firebug/Chrome console to inspect the real value of this object:
console.log(this); // will reveal you the real nature of _this_ ;-)
Probable duplicate for this.
You need to send the ID as the function parameters. Do it like this:
<button id="1" onClick="reply_click(this.id)">B1</button>
<button id="2" onClick="reply_click(this.id)">B2</button>
<button id="3" onClick="reply_click(this.id)">B3</button>
<script type="text/javascript">
function reply_click(clicked_id)
{
alert(clicked_id);
}
</script>
This will send the ID this.id as clicked_id which you can use in your function. See it in action here.