7

I implement library with Google App Script and I have some difficulties to call a function from library using google.script.run.

Here is the code of my Library :

Code.gs

function ShowSideBar() {
    var html = HtmlService.createTemplateFromFile('Index_librairie').evaluate()
        .setTitle('Console de gestion')
        .setWidth(300);
    SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
        .showSidebar(html);
}

function execution_appeler_par_html(){
  Logger.log("execution_appeler_par_html"); 

}

Index_librairie.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
    google.script.run.withSuccessHandler(work_off).execution_appeler_par_html(); 
    function work_off(e){
    alert(e);
    }
    </script>
  </head>
  <body>
    test de ouf
  </body>
</html>

Here is my Spreadsheet that use the Library : Code.gs

function onopen() {
  lbrairietestedouard.ShowSideBar();
}

Google.script.run does not reconize execution_appeler_par_html() function. I should use libraryname.execution_appeler_par_html() but this syntaxe doesn't work in configuration of google.script.run

Rubén
  • 34,714
  • 9
  • 70
  • 166
  • Have you tired a dummy function, which in turn calls `lbrairietestedouard.execution_appeler_par_html()`. Ex: `function dummy(){ lbrairietestedouard.execution_appeler_par_html() }` in your HTML side just `call google.script.run.dummy()` – Jack Brown Feb 22 '18 at 16:33

4 Answers4

11

It seems that google.script.run can't look inside Objects or self-executing anonymous functions. In my case, putting any code inside an object or IIFE resulted in "is not a function" type of error being thrown in the console.

You can work around this by declaring a single function that will call nested methods inside libraries and objects.

.GS file

 function callLibraryFunction(func, args){

    var arr = func.split(".");
    
    var libName = arr[0];
    var libFunc = arr[1];
    
    args = args || [];
       
   return this[libName][libFunc].apply(this, args);

}

In JavaScript, every object behaves like an associative array of key-value pairs, including the global object that 'this' would be pointing to in this scenario. Although both 'libName' and 'libFunc' are of String type, we can still reference them inside the global object by using the above syntax. apply() simply calls the function on 'this', making the result available in global scope.

Here's how you call the library function from the client:

 google.script.run.callLibraryFunction("Library.libraryFunction", [5, 3]);

I don't claim this solution as my own - this is something I saw at Bruce McPherson's website a while back. You could come up with other ad-hoc solutions that may be more appropriate for your case, but I think this one is the most universal.

Rubén
  • 34,714
  • 9
  • 70
  • 166
Anton Dementiev
  • 5,451
  • 4
  • 20
  • 32
  • 1
    I have an html form in parameter, it doesn't propagate with the `this[func].apply(param)` I successfully used this alternative: `eval(func)(param)` – jpep1 Jun 01 '22 at 19:43
1

After quite long time working on this, I found out that any function that you call from the library using google.script.run must exist in both the main script and the library script. The function must at least be declared in the main script while the implementation as well as the parameters are not important. The implementation will be in your library. If the main script does not include the name of the function, the error <your function> is not a function will appear.

Now that there is no meaning of having a library if every single function needs to exist in the main function, the solution provided by @Anton Dementiev would be used as a workaround. Suppose you have a function named testFunc in your library, you can call it using the following method:

google.script.run.callLibraryFunction("testFunc", [5, 3]);

where the library has this function:

function callLibraryFunction(func, args) {
  args = args || [];
  return this[func].apply(this, args);
}

and the main script has this declaration:

function callLibraryFunction() {

}

Google must fix this stupid behaviour

Hans
  • 3,403
  • 3
  • 28
  • 33
1

Noticed the same problem trying to invoque library from google.script.run within HTML code. Here is the workaround I use :

LIBRARY SIDE : aLibrary

function aFunction(){
    //code...;

}

ADDON SIDE (requires library "aLibrary")

function aFunction(){
    aLibrary.aFunction();

}

HTML

<input type="button" value="run aFunction" onclick="google.script.run.aFunction()" />

I like the workaround because I keep a clear view and organisation of my functions names, and do not need to alter HTML code if I bring my functions directly inside the addOn project, once development is satsifying.

Only thing I did not try yet : handling arguments and return values....

I hope this contribution is not to foolish, please forgive me I am very amateur...

Angelo
  • 11
  • 1
0

The issue, as far as I can tell, is due to hoisting - see this answer here.

The difference is that functionOne is a function expression and so only defined when that line is reached, whereas functionTwo is a function declaration and is defined as soon as its surrounding function or script is executed (due to hoisting).

Evidently, google.script.run executes at a level prior to any variable declarations in dependent scripts. So yes, when using callbacks of the form google.script.run.myWhateverCallback() in a library, dependent scripts must define that callback as a hoistable function and not in a variable/constant declaration.

/* don't do this, as you might for your controller actions in the app menu */
const { myWhateverCallback, myOtherCallback } = MyParentLibrary;

/* do this instead, for *every* callback needed by google.script.run */
function myWhateverCallback()
  MyParentLibrary.myWhateverCallback(...arguments);
}

According to prior answers, the body of the function may be redundant since the final execution of the function is in the scope of the parent library - but I haven't tested this, and Google may yet fix it so I'm using the hoisted callback functions as actual wrappers just in case.

Felix Weelix
  • 352
  • 3
  • 7