# HTML-sidebar bouwen in Apps Script [[TOC]] ## HtmlService overzicht HtmlService laat je HTML-pagina's tonen als zijbalk (sidebar) of modaal dialoogvenster in Google Docs, Sheets, Slides en Forms. Zo bouw je een volledige webinterface direct in de Google-applicatie, zonder externe hosting. Je script moet wel container-bound zijn (gekoppeld aan het bestand) om een sidebar of dialoog te kunnen tonen. Er zijn twee manieren om HTML te laden: 1. `HtmlService.createHtmlOutput(htmlString)`: HTML als string in de code. 2. `HtmlService.createHtmlOutputFromFile('bestandsnaam')`: HTML uit een los `.html`-bestand in je project. ```javascript function openSidebar() { const html = HtmlService.createHtmlOutputFromFile('Sidebar') .setTitle('Mijn Sidebar') .setWidth(300); SpreadsheetApp.getUi().showSidebar(html); } function openModaal() { const html = HtmlService.createHtmlOutputFromFile('Modaal') .setWidth(500) .setHeight(400); SpreadsheetApp.getUi().showModalDialog(html, 'Modaal venster'); } ``` ## HTML-bestand aanmaken Maak een bestand `Sidebar.html` aan in je Apps Script-project (via het plus-icoon naast Bestanden, kies HTML): ```html

Data ophalen

``` De `` is belangrijk: zonder dit openen links binnen het beperkte iframe en niet in een nieuw tabblad. ## Server-side functies aanroepen Met `google.script.run` roep je vanuit de HTML een functie aan die in je `.gs`-bestand staat. De naam achter `.run` is letterlijk de functienaam. ```javascript function zoekInSheets(zoekterm) { if (!zoekterm || zoekterm.trim() === '') { throw new Error('Zoekterm is verplicht'); } const blad = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); const data = blad.getDataRange().getValues(); const resultaten = data.filter(rij => rij.some(cel => String(cel).toLowerCase().includes(zoekterm.toLowerCase())) ); return { aantalResultaten: resultaten.length, resultaten: resultaten.slice(0, 10), }; } ``` :::info title="google.script.run is asynchroon" De aanroep blokkeert de browser niet: de interface blijft responsief terwijl de server bezig is. Gebruik daarom altijd `.withSuccessHandler()` en `.withFailureHandler()` om het resultaat of een fout op te vangen. Het serverantwoord komt als eerste argument in je succeshandler binnen. ::: ## Data doorgeven aan HTML bij openen Wil je data al klaarzetten op het moment dat de sidebar opent, gebruik dan een template met `createTemplateFromFile`: ```javascript function openSidebarMetData() { const gebruiker = Session.getActiveUser().getEmail(); const aantalRijen = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getLastRow(); const template = HtmlService.createTemplateFromFile('Sidebar'); template.gebruikerEmail = gebruiker; template.aantalRijen = aantalRijen; const html = template.evaluate().setTitle('Dashboard').setWidth(320); SpreadsheetApp.getUi().showSidebar(html); } ``` In het HTML-bestand lees je de template-variabelen uit met scriptlets: ```html

Ingelogd als:

Rijen in sheet:

``` :::tip title="Scriptlets alleen voor de eerste render" Gebruik scriptlets zoals `` alleen om de pagina bij het openen te vullen. Voor data die later verandert, haal je waarden op via `google.script.run`. Zo blijft je interface snel en hoef je de hele sidebar niet te herladen. ::: ## Formulier in sidebar verwerken ```javascript function verwerkSidebarFormulier(formData) { const naam = formData.naam; const email = formData.email; if (!naam || !email) { throw new Error('Naam en e-mail zijn verplicht'); } const blad = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Aanvragen') || SpreadsheetApp.getActiveSpreadsheet().insertSheet('Aanvragen'); blad.appendRow([new Date(), naam, email, 'Nieuw']); return {succes: true, bericht: `Aanvraag van ${naam} opgeslagen`}; } ``` De bijbehorende HTML met formulier: ```html
``` :::warn title="Valideer altijd ook op de server" Validatie in de browser is alleen voor het gebruiksgemak. Controleer verplichte velden en datatypes opnieuw in je Apps Script-functie, zoals hierboven met de check op `naam` en `email`. Vertrouw nooit blind op invoer vanuit de HTML. ::: ## Praktijkvoorbeeld: data-editor in Sheets :::howto title="Bouw een interactieve data-editor" 1. Maak met `onOpen()` een menu-item dat `openSidebar()` aanroept. 2. Laat de sidebar bij het openen de sheet-data ophalen via `google.script.run` en toon de rijen in een tabel. 3. Voeg per rij knoppen toe voor **bewerken** en **verwijderen**, elk met de rij-index als parameter. 4. Bij een klik roep je een server-functie aan, bijvoorbeeld `werkRijBij(index, waarden)` of `verwijderRij(index)`. 5. De server-functie past de sheet aan met `getRange()` of `deleteRow()`. 6. Ververs in je `withSuccessHandler` de tabel met de bijgewerkte data, zodat de gebruiker direct het resultaat ziet. ::: ## Modaal sluiten en afronden Een modaal dialoogvenster sluit je vanuit de HTML met `google.script.host.close()`. Roep dit aan in je succeshandler nadat de server klaar is, bijvoorbeeld na het opslaan van een formulier. HTML-zijbalken geven je volledige vrijheid in het ontwerp van je interface binnen Google Workspace. Door `google.script.run` te combineren met server-functies bouw je rijke, interactieve tools zonder externe hosting. :::faq ### Kan ik externe CSS-frameworks laden in de sidebar? Ja, maar alleen via HTTPS. Voeg een ``-tag toe in de `` van je HTML-bestand. Let op dat het beveiligde iframe en de Content Security Policy van Google sommige externe bronnen kunnen blokkeren; test dit dus altijd. ### Hoe sluit ik een modal programmatisch? Roep `google.script.host.close()` aan vanuit de JavaScript in je HTML-bestand. Dit sluit het dialoogvenster of de sidebar van waaruit de code draait. ### Kan ik afbeeldingen tonen in een sidebar? Ja. Verwijs naar een externe HTTPS-URL of een gedeelde Drive-link in een ``-tag. Ingebedde afbeeldingen als base64 in de `src` werken ook, maar maken de pagina groter en langzamer. ### Wat is het verschil tussen showSidebar en showModalDialog? `showSidebar` toont een vast paneel aan de zijkant dat de gebruiker niet blokkeert: de spreadsheet blijft bruikbaar. `showModalDialog` opent een venster dat de interface blokkeert tot de gebruiker het sluit. ### Waarom werkt mijn google.script.run-aanroep niet? Controleer of het script container-bound is, of de functienaam exact klopt (hoofdlettergevoelig) en of de gebruiker de juiste toestemmingen heeft gegeven. Een fout op de server belandt in je `withFailureHandler`, dus log daar de melding om de oorzaak te zien. ### Kan ik dezelfde HTML gebruiken in Docs en Sheets? Het HTML-bestand zelf is herbruikbaar, maar de aanroepen verschillen: gebruik `SpreadsheetApp.getUi()` in Sheets en `DocumentApp.getUi()` in Docs. Houd je server-functies daarom gescheiden per applicatie. :::