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:
HtmlService.createHtmlOutput(htmlString): HTML als string in de code.HtmlService.createHtmlOutputFromFile('bestandsnaam'): HTML uit een los.html-bestand in je project.
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):
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500&display=swap" rel="stylesheet">
<style>
body { font-family: 'Google Sans', sans-serif; padding: 16px; }
.knop { background: #1A73E8; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; }
.resultaat { margin-top: 12px; padding: 8px; background: #f8f9fa; border-radius: 4px; }
</style>
</head>
<body>
<h3>Data ophalen</h3>
<input type="text" id="zoekterm" placeholder="Zoekterm..." style="width:100%;margin-bottom:8px">
<button class="knop" onclick="zoekData()">Zoeken</button>
<div id="resultaat" class="resultaat"></div>
<script>
function zoekData() {
const term = document.getElementById('zoekterm').value;
document.getElementById('resultaat').textContent = 'Laden...';
google.script.run
.withSuccessHandler(toonResultaat)
.withFailureHandler(toonFout)
.zoekInSheets(term);
}
function toonResultaat(data) {
const el = document.getElementById('resultaat');
el.textContent = JSON.stringify(data, null, 2);
}
function toonFout(fout) {
document.getElementById('resultaat').textContent = 'Fout: ' + fout.message;
}
</script>
</body>
</html>
De <base target="_top"> 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.
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),
};
}
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:
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:
<p>Ingelogd als: <?= gebruikerEmail ?></p>
<p>Rijen in sheet: <?= aantalRijen ?></p>
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
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:
<form onsubmit="verstuurFormulier(event)">
<input name="naam" placeholder="Naam" required>
<input name="email" type="email" placeholder="E-mail" required>
<button type="submit">Versturen</button>
</form>
<script>
function verstuurFormulier(event) {
event.preventDefault();
const data = Object.fromEntries(new FormData(event.target));
google.script.run
.withSuccessHandler(r => alert(r.bericht))
.withFailureHandler(e => alert(e.message))
.verwerkSidebarFormulier(data);
}
</script>
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
Bouw een interactieve data-editor
- Maak met
onOpen()een menu-item datopenSidebar()aanroept. - Laat de sidebar bij het openen de sheet-data ophalen via
google.script.runen toon de rijen in een tabel. - Voeg per rij knoppen toe voor bewerken en verwijderen, elk met de rij-index als parameter.
- Bij een klik roep je een server-functie aan, bijvoorbeeld
werkRijBij(index, waarden)ofverwijderRij(index). - De server-functie past de sheet aan met
getRange()ofdeleteRow(). - Ververs in je
withSuccessHandlerde 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.
Kan ik externe CSS-frameworks laden in de sidebar?
Ja, maar alleen via HTTPS. Voeg een <link>-tag toe in de <head> 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 <img>-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.