Service HTML: communication avec les fonctions du serveur

google.script.run est une API JavaScript asynchrone côté client qui permet aux pages de service HTML d'appeler des fonctions Apps Script côté serveur. L'exemple suivant montre la fonctionnalité la plus élémentaire de google.script.run : appeler une fonction sur le serveur à partir de JavaScript côté client.

Code.gs

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

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

Index.html

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

Si vous déployez ce script en tant qu'application Web et que vous accédez à son URL, vous ne verrez rien. Toutefois, si vous consultez les journaux, vous verrez que la fonction de serveur doSomething a été appelée.

Les appels côté client aux fonctions côté serveur sont asynchrones : une fois que le navigateur a demandé au serveur d'exécuter la fonction doSomething, il passe immédiatement à la ligne de code suivante sans attendre de réponse. Cela signifie que les appels de fonctions de serveur peuvent ne pas s'exécuter dans l'ordre prévu. Si vous effectuez deux appels de fonction en même temps, il est impossible de savoir quelle fonction s'exécute en premier. Le résultat peut varier à chaque fois que vous chargez la page. Dans ce cas, les gestionnaires de réussite et les gestionnaires d'échec vous aident à contrôler le flux de votre code.

L'API google.script.run autorise 10 appels simultanés aux fonctions de serveur. Si vous effectuez un 11e appel alors que 10 sont toujours en cours d'exécution, la fonction de serveur est retardée jusqu'à ce que l'un des 10 emplacements soit libéré. En pratique, vous devriez rarement avoir à vous soucier de cette restriction, d'autant plus que la plupart des navigateurs limitent déjà le nombre de requêtes simultanées au même serveur à un nombre inférieur à 10. Dans Firefox, par exemple, la limite est de 6. La plupart des navigateurs retardent de la même manière les requêtes serveur excédentaires jusqu'à ce que l'une des requêtes existantes soit terminée.

Paramètres et valeurs renvoyées

Appelez une fonction de serveur avec des paramètres provenant du client. De même, une fonction de serveur peut renvoyer une valeur au client en tant que paramètre transmis à un gestionnaire de réussite.

Les paramètres et les valeurs de retour légaux sont des primitives JavaScript telles que Number, Boolean, String ou null, ainsi que des objets et des tableaux JavaScript composés de primitives, d'objets et de tableaux. Un élément form sur la page peut également être utilisé comme paramètre, mais il doit être le seul paramètre de la fonction et ne peut pas être utilisé comme valeur de retour. Les requêtes échouent si vous tentez de transmettre un élément Date, Function ou DOM autre qu'un élément form, ou un autre type interdit, y compris les types interdits à l'intérieur d'objets ou de tableaux. Les objets qui créent des références circulaires échouent également, et les champs non définis dans les tableaux deviennent null.

Notez qu'un objet transmis au serveur devient une copie de l'original. Si une fonction de serveur reçoit un objet et modifie ses propriétés, les propriétés du client ne sont pas affectées.

Gestionnaires de réussite

Étant donné que les appels google.script.run sont asynchrones, le code côté client passe à la ligne suivante sans attendre de réponse. Pour spécifier une fonction de rappel qui s'exécute lorsque le serveur répond, utilisez withSuccessHandler(function). Si la fonction de serveur renvoie une valeur, l'API transmet cette valeur à la fonction de rappel en tant que paramètre.

L'exemple suivant affiche une alerte du navigateur lorsque le serveur répond. Cet exemple de code nécessite une autorisation, car la fonction côté serveur accède à votre compte Gmail. Pour autoriser le script, exécutez manuellement la fonction getUnreadEmails depuis l'éditeur de scripts une fois avant de charger la page. Vous pouvez également déployer l'application Web pour qu'elle s'exécute en tant qu;"utilisateur accédant à l'application Web". Dans ce cas, vous êtes invité à fournir une autorisation lorsque vous chargez l'application.

Code.gs

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

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

Index.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>

Gestionnaires d'échecs

Si le serveur ne répond pas ou génère une erreur, withFailureHandler(function) vous permet de spécifier un gestionnaire d'échec à exécuter à la place d'un gestionnaire de réussite. En cas d'erreur, l'API transmet l'objet Error en tant qu'argument au gestionnaire d'échecs.

Par défaut, si vous ne spécifiez pas de gestionnaire d'échecs, les échecs sont consignés dans la console JavaScript. Pour remplacer ce comportement, appelez withFailureHandler(null) ou fournissez un gestionnaire d'échec qui ne fait rien.

La syntaxe des gestionnaires d'échec est presque identique à celle des gestionnaires de réussite, comme le montre cet exemple.

Code.gs

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

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

Index.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>

Objets User

Pour réutiliser le même gestionnaire de réussite ou d'échec pour plusieurs appels au serveur, appelez withUserObject(object) pour spécifier un objet qui est transmis au gestionnaire en tant que deuxième paramètre. Cet "objet utilisateur", à ne pas confondre avec la classe User, vous permet de répondre au contexte dans lequel le client a contacté le serveur. Étant donné que les objets utilisateur ne sont pas envoyés au serveur, ils peuvent être presque n'importe quoi, y compris des fonctions et des éléments DOM, sans les restrictions sur les paramètres et les valeurs renvoyées pour les appels de serveur. Les objets utilisateur ne peuvent pas être des objets construits avec l'opérateur new.

Dans cet exemple, le fait de cliquer sur l'un des deux boutons met à jour ce bouton avec une valeur provenant du serveur, tout en laissant l'autre bouton inchangé, même s'ils partagent un gestionnaire de réussite. Dans le gestionnaire onclick, le mot clé this fait référence à button lui-même.

Code.gs

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

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

Index.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>

Formulaires

Si vous appelez une fonction de serveur avec un élément form comme paramètre, le formulaire devient un objet unique avec les noms de champs comme clés et les valeurs de champs comme valeurs. Toutes les valeurs sont converties en chaînes, à l'exception du contenu des champs d'entrée de fichier, qui deviennent des objets Blob.

Cet exemple traite un formulaire, y compris un champ de saisie de fichier, sans recharger la page. Il importe le fichier dans Google Drive, puis imprime l'URL du fichier sur la page côté client. Dans le gestionnaire onsubmit, le mot clé this fait référence au formulaire lui-même. Notez que, lors du chargement, l'action d'envoi par défaut de tous les formulaires de la page est désactivée par preventFormSubmit. Cela empêche la page d'être redirigée vers une URL inexacte en cas d'exception.

Code.gs

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

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

Index.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>

Exécuteurs de script

Considérez google.script.run comme un outil de création pour un "exécuteur de scripts". Si vous ajoutez un gestionnaire de réussite, un gestionnaire d'échec ou un objet utilisateur à un exécuteur de script, vous ne modifiez pas l'exécuteur existant. Au lieu de cela, vous obtenez un nouvel exécuteur de script avec un nouveau comportement.

Utilisez n'importe quelle combinaison et n'importe quel ordre de withSuccessHandler, withFailureHandler et withUserObject. Appelez également l'une des fonctions de modification sur un exécuteur de script pour lequel une valeur est déjà définie. La nouvelle valeur remplace la valeur précédente.

Cet exemple définit un gestionnaire d'échec commun pour les trois appels de serveur, mais deux gestionnaires de réussite distincts :

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

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

Fonctions privées

Les fonctions serveur dont le nom se termine par un trait de soulignement sont considérées comme privées. Ces fonctions ne peuvent pas être appelées par google.script et leurs noms ne sont jamais envoyés au client. Vous pouvez les utiliser pour masquer les détails d'implémentation qui doivent rester secrets sur le serveur. google.script ne peut pas non plus voir les fonctions dans les bibliothèques ni les fonctions non déclarées au niveau supérieur du script.

Dans cet exemple, la fonction getBankBalance est disponible dans le code client. Un utilisateur qui inspecte votre code source peut découvrir son nom même si vous ne l'appelez pas. Toutefois, les fonctions deepSecret_ et obj.objectMethod sont complètement invisibles pour le client.

Code.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
  }
};

Index.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>

Redimensionner les boîtes de dialogue dans les applications Google Workspace

Les boîtes de dialogue personnalisées dans Google Docs, Google Sheets ou Forms peuvent être redimensionnées en appelant les méthodes google.script.host setWidth(width) ou setHeight(height) dans le code côté client. (Pour définir la taille initiale d'une boîte de dialogue, utilisez les méthodes HtmlOutput setWidth(width) et setHeight(height).) Notez que les boîtes de dialogue ne sont pas recentrées dans la fenêtre parente lorsqu'elles sont redimensionnées, et qu'il n'est pas possible de redimensionner les barres latérales.

Fermer les boîtes de dialogue et les barres latérales dans Google Workspace

Si vous utilisez le service HTML pour afficher une boîte de dialogue ou une barre latérale dans Google Docs, Sheets ou Forms, vous ne pouvez pas fermer l'interface en appelant window.close. Vous devez plutôt appeler google.script.host.close. Pour obtenir un exemple, consultez la section sur l'affichage de code HTML en tant qu'interface utilisateur Google Workspace.

Déplacer la sélection du navigateur dans Google Workspace

Pour faire passer le focus du navigateur de l'utilisateur d'une boîte de dialogue ou d'une barre latérale à l'éditeur Google Docs, Sheets ou Forms, appelez la méthode google.script.host.editor.focus. Cette méthode est particulièrement utile en combinaison avec les méthodes Document service Document.setCursor(position) et Document.setSelection(range).