Servizio HTML: comunicazione con le funzioni server

google.script.run è un'API JavaScript lato client asincrona che consente alle pagine del servizio HTML di chiamare le funzioni Apps Script lato server. L'esempio seguente mostra la funzionalità di base di google.script.run: chiamare una funzione sul server da JavaScript lato client.

Codice.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function doSomething() {
  Logger.log('I was called!');
}

Indice.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      google.script.run.doSomething();
    </script>
  </head>
</html>

Se esegui il deployment di questo script come applicazione web e visiti l'URL, non vedrai nulla; tuttavia, se visualizzi i log, vedrai che la funzione server doSomething() è stata chiamata.

Le chiamate lato client alle funzioni lato server sono asincrone: dopo che il browser richiede che il server esegua la funzione doSomething(), il browser passa immediatamente alla riga di codice successiva senza attendere una risposta. Ciò significa che le chiamate delle funzioni del server potrebbero non essere eseguite nell'ordine previsto. Se effettui due chiamate di funzione contemporaneamente, non è possibile sapere quale funzione verrà eseguita per prima; il risultato potrebbe essere diverso ogni volta che carichi la pagina. In questa situazione, i gestori riusciti e i gestori degli errori contribuiscono a controllare il flusso del codice.

L'API google.script.run consente 10 chiamate simultanee alle funzioni del server. Se effettui un'undicesima chiamata mentre 10 sono ancora in esecuzione, la funzione server verrà ritardata fino a quando non verrà liberato uno dei 10 spot. In pratica, dovresti pensare raramente a questa restrizione, soprattutto perché la maggior parte dei browser limita già il numero di richieste in parallelo allo stesso server a un numero inferiore a 10. In Firefox, ad esempio, il limite è 6. La maggior parte dei browser ritarda in modo analogo le richieste in eccesso del server fino al completamento di una delle richieste esistenti.

Parametri e valori restituiti

Puoi chiamare una funzione del server con i parametri del client. Analogamente, una funzione server può restituire un valore al client come parametro passato a un gestore riuscito.

I parametri legali e i valori restituiti sono primitive JavaScript come Number, Boolean, String o null, nonché oggetti e array JavaScript composti da primitivi, oggetti e array. Anche un elemento form all'interno della pagina è legale come parametro, ma deve essere l'unico parametro della funzione e non è legale come valore restituito. Le richieste non vanno a buon fine se provi a trasmettere un elemento Date, Function, DOM oltre a form o a un altro tipo vietato, inclusi i tipi vietati all'interno di oggetti o array. Anche gli oggetti che creano riferimenti circolari non andranno a buon fine e i campi non definiti all'interno degli array diventano null.

Tieni presente che un oggetto passato al server diventa una copia dell'originale. Se una funzione server riceve un oggetto e ne modifica le proprietà, le proprietà sul client non sono interessate.

Gestori riusciti

Poiché il codice lato client continua nella riga successiva senza attendere il completamento di una chiamata al server, withSuccessHandler(function) consente di specificare una funzione di callback lato client da eseguire quando il server risponde. Se la funzione server restituisce un valore, l'API passa il valore alla nuova funzione come parametro.

L'esempio seguente mostra un avviso del browser quando il server risponde. Tieni presente che questo esempio di codice richiede l'autorizzazione perché la funzione lato server accede al tuo account Gmail. Il modo più semplice per autorizzare lo script è eseguire la funzione getUnreadEmails() manualmente dall'editor di script una volta prima di caricare la pagina. In alternativa, quando esegui il deployment dell'app web, puoi scegliere di eseguirla come "utente che accede all'app web", nel qual caso ti verrà chiesta l'autorizzazione quando carichi l'app.

Codice.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function getUnreadEmails() {
  return GmailApp.getInboxUnreadCount();
}

Indice.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function onSuccess(numUnread) {
        var div = document.getElementById('output');
        div.innerHTML = 'You have ' + numUnread
            + ' unread messages in your Gmail inbox.';
      }

      google.script.run.withSuccessHandler(onSuccess)
          .getUnreadEmails();
    </script>
  </head>
  <body>
    <div id="output"></div>
  </body>
</html>

Gestori errori

Se il server non risponde o restituisce un errore, withFailureHandler(function) ti consente di specificare un gestore di errori anziché un gestore di successo, con l'oggetto Error (se presente) passato come argomento.

Per impostazione predefinita, se non specifichi un gestore di errori, gli errori vengono registrati nella console JavaScript. Per eseguire l'override di questa funzionalità, chiama withFailureHandler(null) o fornisci un gestore di errori che non esegue alcuna operazione.

La sintassi per i gestori di errori è quasi identica a quella dei gestori di successo, come mostra questo esempio.

Codice.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function getUnreadEmails() {
  // 'got' instead of 'get' will throw an error.
  return GmailApp.gotInboxUnreadCount();
}

Indice.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function onFailure(error) {
        var div = document.getElementById('output');
        div.innerHTML = "ERROR: " + error.message;
      }

      google.script.run.withFailureHandler(onFailure)
          .getUnreadEmails();
    </script>
  </head>
  <body>
    <div id="output"></div>
  </body>
</html>

Oggetti utente

Puoi riutilizzare lo stesso gestore di operazione riuscita o di errore per più chiamate al server chiamando withUserObject(object) per specificare un oggetto che verrà passato al gestore come secondo parametro. Questo "oggetto utente" (da non confondere con la classe User) consente di rispondere al contesto in cui il client ha contattato il server. Poiché gli oggetti utente non vengono inviati al server, possono essere praticamente qualsiasi cosa, inclusi funzioni, elementi DOM e così via, senza le limitazioni relative ai parametri e ai valori di ritorno per le chiamate al server. Tuttavia, gli oggetti utente non possono essere oggetti creati con l'operatore new.

In questo esempio, facendo clic su uno di questi due pulsanti, il pulsante viene aggiornato con un valore dal server, mentre l'altro pulsante rimane invariato, anche se condividono un solo gestore riuscito. All'interno del gestore onclick, la parola chiave thisfa riferimento al button stesso.

Codice.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function getEmail() {
  return Session.getActiveUser().getEmail();
}

Indice.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function updateButton(email, button) {
        button.value = 'Clicked by ' + email;
      }
    </script>
  </head>
  <body>
    <input type="button" value="Not Clicked"
      onclick="google.script.run
          .withSuccessHandler(updateButton)
          .withUserObject(this)
          .getEmail()" />
    <input type="button" value="Not Clicked"
      onclick="google.script.run
          .withSuccessHandler(updateButton)
          .withUserObject(this)
          .getEmail()" />
  </body>
</html>

Moduli

Se chiami una funzione server con un elemento form come parametro, il modulo diventa un singolo oggetto con i nomi dei campi come chiavi e i valori dei campi come valori. Tutti i valori vengono convertiti in stringhe, ad eccezione dei contenuti dei campi di input file, che diventano oggetti Blob.

Questo esempio elabora un modulo, incluso un campo di immissione del file, senza ricaricare la pagina; carica il file su Google Drive e stampa l'URL del file nella pagina lato client. All'interno del gestore onsubmit, la parola chiave this si riferisce al modulo stesso. L'azione predefinita per l'invio al momento del caricamento di tutti i moduli nella pagina è disabilitata da preventFormSubmit. In questo modo si impedisce il reindirizzamento della pagina a un URL non preciso in caso di eccezione.

Codice.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function processForm(formObject) {
  var formBlob = formObject.myFile;
  var driveFile = DriveApp.createFile(formBlob);
  return driveFile.getUrl();
}

Indice.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      // Prevent forms from submitting.
      function preventFormSubmit() {
        var forms = document.querySelectorAll('form');
        for (var i = 0; i < forms.length; i++) {
          forms[i].addEventListener('submit', function(event) {
            event.preventDefault();
          });
        }
      }
      window.addEventListener('load', preventFormSubmit);

      function handleFormSubmit(formObject) {
        google.script.run.withSuccessHandler(updateUrl).processForm(formObject);
      }
      function updateUrl(url) {
        var div = document.getElementById('output');
        div.innerHTML = '<a href="' + url + '">Got it!</a>';
      }
    </script>
  </head>
  <body>
    <form id="myForm" onsubmit="handleFormSubmit(this)">
      <input name="myFile" type="file" />
      <input type="submit" value="Submit" />
    </form>
    <div id="output"></div>
 </body>
</html>

Esecutori script

Puoi considerare google.script.run come uno strumento per la creazione di uno script runner. Se aggiungi un gestore di operazione riuscita, un gestore di errori o un oggetto utente a uno script runner, non cambi il runner esistente, ma un nuovo esecutore con un nuovo comportamento.

Puoi utilizzare qualsiasi combinazione e qualsiasi ordine di withSuccessHandler(), withFailureHandler() e withUserObject(). Puoi anche chiamare una qualsiasi delle funzioni di modifica su un esecutore di script che ha già un valore impostato. Il nuovo valore sostituisce semplicemente quello precedente.

In questo esempio viene impostato un gestore degli errori comune per tutte e tre le chiamate al server, ma due gestori riusciti separati:

var myRunner = google.script.run.withFailureHandler(onFailure);
var myRunner1 = myRunner.withSuccessHandler(onSuccess);
var myRunner2 = myRunner.withSuccessHandler(onDifferentSuccess);

myRunner1.doSomething();
myRunner1.doSomethingElse();
myRunner2.doSomething();

Funzioni private

Le funzioni del server i cui nomi terminano con un trattino basso sono considerate private. Queste funzioni non possono essere chiamate da google.script e i loro nomi non vengono mai inviati al client. Puoi quindi utilizzarle per nascondere i dettagli di implementazione che devono essere manteniti segreti sul server. Inoltre, google.script non può vedere le funzioni all'interno delle librerie e quelle che non sono dichiarate al livello più alto dello script.

In questo esempio, la funzione getBankBalance() è disponibile nel codice client; un utente che controlla il tuo codice sorgente può rilevarne il nome anche se non lo chiami. Tuttavia, le funzioni deepSecret_() e obj.objectMethod() sono completamente invisibili al client.

Codice.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function getBankBalance() {
  var email = Session.getActiveUser().getEmail()
  return deepSecret_(email);
}

function deepSecret_(email) {
 // Do some secret calculations
 return email + ' has $1,000,000 in the bank.';
}

var obj = {
  objectMethod: function() {
    // More secret calculations
  }
};

Indice.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function onSuccess(balance) {
        var div = document.getElementById('output');
        div.innerHTML = balance;
      }

      google.script.run.withSuccessHandler(onSuccess)
          .getBankBalance();
    </script>
  </head>
  <body>
    <div id="output">No result yet...</div>
  </body>
</html>

Ridimensionamento delle finestre di dialogo nelle Google Workspace applicazioni

Le caselle di dialogo personalizzate in Documenti, Fogli o Moduli Google possono essere ridimensionate chiamando i metodi google.script.host setWidth(width) o setHeight(height) nel codice lato client. Per impostare le dimensioni iniziali di una finestra di dialogo, utilizza i metodi HtmlOutput setWidth(width) e setHeight(height). Tieni presente che le finestre di dialogo non vengono centrate nuovamente nella finestra principale quando vengono ridimensionate e non è possibile ridimensionare le barre laterali.

Chiudi finestre di dialogo e barre laterali in Google Workspace

Se utilizzi il servizio HTML per visualizzare una finestra di dialogo o una barra laterale in Documenti, Fogli o Moduli Google, non puoi chiudere l'interfaccia chiamando window.close(). Devi invece chiamare google.script.host.close(). Per un esempio, consulta la sezione sulla pubblicazione di HTML come Google Workspace interfaccia utente.

Spostamento dello stato attivo del browser in Google Workspacein corso...

Per spostare lo stato attivo nel browser dell'utente da una finestra di dialogo o una barra laterale all'editor di Documenti, Fogli o Moduli Google, è sufficiente chiamare il metodo google.script.host.editor.focus(). Questo metodo è particolarmente utile in combinazione con i metodi di servizio per documenti Document.setCursor(position) e Document.setSelection(range).