0

I have 2 Google Apps Script Projects.

  1. SheetsLib - This is a Library which I created, it contains my go-to functions.
  2. TestFile - This is a container-bound script, which utilizes the SheetsLib.

SheetsLib contains the following functions which are relevant for this question:

  1. displayDraftsSelector - Displays the draftSelector Html Page in the Sidebar.
  2. draftSelector - Html file, which contains a js script as well that calls back-end function to populate a <select>
  3. getDraftsArr - Server function which returns all gmail drafts from the user.

The SheetsLib function(s) do work, i.e. I have test functions to confirm that. My goal is to enhance this library, so that I can use it in multiple projects with the functionality to allow a user to choose an existing Gmail Draft and send it to selected users (in the active Spreadsheet).

PROBLEM In my new container-bound script, which has access to the Library, I can only show the sidebar but not call a back-end function (which resides in the Library) when I press a button in sidebar:

  1. I load the view successfully using displayDraftsSelector() which shows the view draftSelector. This is all functionality from the Library.
  2. Then, the view calls the getDraftsArr() and this is what gets the error. But that function does exist in the Library (and it does work as intended).

The following is the error I see in the console when the sidebar loads:

Uncaught TypeError: google.script.run.withSuccessHandler(...).withFailureHandler(...).getDraftsArr is not a function

What should happen ideally is that, the back-end function getDraftsArr() is called and its result populates the select item. Then the user can select one draft in the sidebar. When the user confirms using a button, the active rows are the recipients. Overall, this all works when I copy-> paste, but what cannot figure out is how to keep the functionality in a library.

The following is the function located in the Library which I am trying to call.

    // back-end in Library Project
    function getDraftsArr(){
      let output = [];
      const messages = GmailApp.getDraftMessages();
      
      messages.forEach( message => {  
        output.push({
          id: message.getId(),
          subject: message.getSubject()
        });
      });
      
      return JSON.stringify(output)  
    }

The following is in the back-end of the library

    // front-end in Library Project    
    <select id="draftsSelect"></select>
 
    <script>
    function getDrafts(){
    const draftsSelect = document.getElementById("draftsSelect");
    google.script.run
      .withSuccessHandler( updateDrafts )
      .getDraftsArr();         
              
    function updateDrafts( drafts ){                
      var options = "";
      var draftsParsed = JSON.parse(drafts);        
                  
      draftsParsed.forEach( draft => {                   
        options += "<option value='" + draft.id + "'>" + draft.subject + "</option>";
      });
      draftsSelect.innerHTML = options;        }                          
    }
    </script>
Marios
  • 26,333
  • 8
  • 32
  • 52
Neven Subotic
  • 1,399
  • 1
  • 6
  • 18
  • Related [Using Google Apps Script Libraries](https://stackoverflow.com/q/44784024/1595451) – Rubén Jun 20 '20 at 17:00
  • Does this answer your question? [Is there any way to call a library from a client side user interface?](https://stackoverflow.com/questions/22889747/is-there-any-way-to-call-a-library-from-a-client-side-user-interface) – Rubén Jun 20 '20 at 17:05
  • @Rubén It does not work for me because the html file is part of the library project in my case. In the case mentioned in the link, the html is part of the container-bound project (not the library). – Neven Subotic Jun 20 '20 at 21:35
  • How about this one https://stackoverflow.com/q/31034303/1595451? – Rubén Jun 20 '20 at 21:37
  • 1
    Or this other one https://stackoverflow.com/q/59271952/1595451? – Rubén Jun 20 '20 at 21:57

2 Answers2

0

If you can create a dummy function. Then the dummy function to create is this one:

function executeLibraryFunctionsByName(funcname) {
  libraryname[funcname]();
}

This function will allow you to call all of your library functions by name. I'm not sure that this will work if your libary name has a space in it. In that case change it's name. Note: don't put quotations around the libraryname. Just write the libaryname with no quotes and I would avoid spaces and it becomes the 'this' for your function.

As an example I have a library xyzSUS1 and I can use a button like this:

<input type="button" value="test" onClick="google.script.run.callLibraryFunctionsByName('selectColumnsSkipHeader');" />

in my sidebar to run the library function xyzSUS1.selectColumnsSkipHeader() .

the command in function declaration in my gs code is this:

function callLibraryFunctionsByName(funcname) {
  xyzSUS1[funcname]();//the this for funcname because xyzSUS1
}
Cooper
  • 59,616
  • 6
  • 23
  • 54
  • Where should that `executeLibraryFunctionsByName ()` go and which of the functions should it call? – Neven Subotic Jun 20 '20 at 21:51
  • It can go into any .gs file and it can call any function in your library. – Cooper Jun 20 '20 at 22:26
  • The next time your using your debugger click in the this. You will find that your library name is a global variable – Cooper Jun 21 '20 at 14:40
  • I am trying to understand how you are doing this but I think your solution is for a different case. In your case, you create the `callLibraryFunctionsByName()` in the container-bound-project as well as the HTML file, and then you call the functions from the library using your technique. What I want to do, is have all the functionality, including the HTML File in the Library and then have an easy way to use the library in different projects. Did I understand that as you meant it? – Neven Subotic Jun 29 '20 at 06:43
  • No actually I don't create a function in the html I simply use google.script.run which calls any server function. And then the function call within that function has the command prefixed with the global variable of the library name. The global variable with the library name is created when you add the library to you script resources. It's similarr to what ruben is doing except that you can call any of your library functions that way. – Cooper Jun 29 '20 at 14:28
  • I still cannot get it to work. I added the `callLibraryFunctionsByName()` to my project, but not my Library. That did not work. I get `ScriptError: Script function not found: callLibraryFunctionsByName`. Then I added the `callLibraryFunctionsByName()` to my Library as well. That did not work. I cannot call itsself as a Library. `ScriptError: ReferenceError: SheetsLib is not defined`. – Neven Subotic Jun 30 '20 at 08:15
0

Thanks to @Rubén for the link to this: stackoverflow.com/q/59271952/1595451

Basically the solution was to create a function in my container-script with the same name as the back-end function which are in the library, which then call the library.

So in my Library I had getDraftsArr(){}before, and now I added the following to my container-bound project:

function getDraftsArr(){
  return SheetsLib.getDraftsArr()
}

That did the trick.

Neven Subotic
  • 1,399
  • 1
  • 6
  • 18