Gebeurtenissen en de SDK
De gebeurtenissen van de widgets en van het scherm, de code-editor, de vooraf bepaalde acties en de keplin-SDK in TypeScript — gegevens, widgets, navigatie, sessie, modale vensters en workflows.
Er is heel wat scherm dat u maakt zonder één regel te schrijven: een datastore koppelen, widgets slepen, een knop op een ander scherm richten. Maar vroeg of laat verschijnt het "wanneer dit gebeurt, doe dan dat" — opslaan en teruggaan, een tabel opnieuw laden na een filter, bevestigen voor het verwijderen, een modaal venster openen en gebruiken wat het teruggaf.
Daar dienen de gebeurtenissen voor: punten op het scherm waar uw eigen code
draait, geschreven in TypeScript, met een SDK — het object keplin — die
toegang geeft tot alles wat het scherm heeft.
Waar de gebeurtenissen staan
In de inspector heet de laatste categorie van een widget (en van het scherm zelf) Gebeurtenissen. Zij heeft één regel per beschikbare gebeurtenis, en op elke regel:
- een stip links: gevuld wanneer die gebeurtenis al code heeft, leeg wanneer niet;
- een knop … rechts, die de editor opent.

De namen van de gebeurtenissen worden niet vertaald — zij zijn in elke taal
dezelfde (onClick, onRowClick, onLoad), omdat het ook de namen zijn die in
de code en in de registraties van Radar verschijnen.
De gebeurtenissen van het scherm
Klik op een lege zone van het canvas zodat de inspector het scherm toont. De categorie Gebeurtenissen heeft er drie:
| Gebeurtenis | Wanneer die afgaat | Waarvoor |
|---|---|---|
onLoad |
Eén keer, wanneer het scherm opent. | State klaarzetten, dingen laden die de datastores niet laden, welkom heten. |
onParamsChange |
Telkens wanneer de routeparameters wijzigen — en niet bij het eerste openen. | Reageren op een wisseling van record zonder het scherm opnieuw te openen. |
onUnload |
Wanneer het scherm afsluit. | State opruimen, concepten bewaren. |

De gebeurtenissen van elke widget
Elk type widget declareert de zijne. Naast de naam telt de payload — de
gegevens die de gebeurtenis meebrengt, en die de code leest in keplin.event.
Formuliervelden
| Widget | Gebeurtenissen | keplin.event |
|---|---|---|
| Tekstvak, Tekstgebied, Getal, Ja/Nee, Keuzelijst, Datum, Kleur | onChange |
{ value } |
| Bestand | onChange, onUpload |
onUpload: { file, name } |
Acties en navigatie
| Widget | Gebeurtenissen | keplin.event |
|---|---|---|
| Knop | onClick |
{} |
| Knop met menu | onClick, onMenuItem |
onMenuItem: { id, label } |
| Link | onClick |
{} |
| Exporteren | onExport, onDataLoaded |
onExport: { rows, filename } |
Structuur en inhoud
| Widget | Gebeurtenissen | keplin.event |
|---|---|---|
| Tabbladen | onTabChange |
{ tab } |
| Rapport | onLoad |
{ report } |
Gegevenswidgets
| Widget | Gebeurtenissen | keplin.event |
|---|---|---|
| Tabel | onRowClick, onRowDoubleClick, onSelectionChange, onDataLoaded |
{ row, index } · onSelectionChange: { row, rows } |
| Lijst, Kaarten | onRowClick, onDataLoaded |
{ row, index } |
| Grafiek | onClick |
{ name, seriesName, value, dataIndex } |
| KPI | onClick, onDataLoaded |
{ value, indicatorId } |
Borden en planning
| Widget | Gebeurtenissen | keplin.event |
|---|---|---|
| Kanban | onCardClick, onCardCreate, onCardMoved, onDataLoaded |
{ row } · { column } · { row, from, to, index } |
| Kalender | onEventClick, onDayClick, onRangeSelect, onRangeChange, onDataLoaded |
{ row } · { date } · { start, end } · { start, end, view } |
| Gantt | onBarClick, onEmptyClick, onDataLoaded |
{ row } · { date } |
Processen
| Widget | Gebeurtenissen | keplin.event |
|---|---|---|
| Status van het proces | onDecide |
{ task, outcome } |
| Mijn taken | onOpen, onDecide |
{ task, screenId } |
Nota
Widgets die u zelf hebt geprogrammeerd (die op het palet onder Aangepast verschijnen) declareren hun eigen gebeurtenissen, en die verschijnen hier als alle andere.
De code-editor
De knop … van een gebeurtenis opent de editor in een modaal venster. De titel
zegt waar u bent: het id van de widget (of de naam van het scherm) en de naam
van de gebeurtenis — w_fic_sav1 · onClick.

| Knop | Wat die doet |
|---|---|
| Actie invoegen | Schrijft voor u de code van een veelvoorkomende taak (hierna). |
| Handler verwijderen | Wist de code van deze gebeurtenis. De stip wordt weer leeg. |
| Annuleren | Sluit zonder op te slaan. |
| Opslaan | Controleert en slaat op. |
De editor heeft suggesties tijdens het typen (Ctrl+Spatie): de hele
keplin is gedeclareerd, met de juiste types — en, nog beter, de id's van de
widgets van dit scherm zitten erin. keplin.widgets.get(" typen toont de
lijst met widgets van het scherm, en een id dat niet bestaat wordt als fout
aangewezen voordat u opslaat.
Atenção
Bij het opslaan wordt de code gecompileerd. Is zij niet uitvoerbaar, dan weigert het platform haar — De gebeurtenis is niet opgeslagen: de code is niet uitvoerbaar — en blijft het venster open zodat u kunt herstellen. Een scherm blijft nooit met kapotte code achter.
De vooraf bepaalde acties
Actie invoegen opent een lijst met de meest voorkomende taken. U kiest er een en de code wordt achter het bestaande aan geschreven, meteen met de echte namen van uw scherm — de eerste record-datastore, de eerste tabel, het eerste tekstvak.

| Actie | Wat die schrijft |
|---|---|
| Datastore opslaan | Valideert en slaat het record op, met een melding bij succes. |
| Datastore opnieuw laden | Leest de gegevens van een datastore opnieuw. |
| Datastore filteren (op tekst) | Leest de tekst uit een tekstvak en past die als filter toe. |
| Naar een scherm navigeren | Springt naar een andere route van de app. |
| Een tabel opnieuw laden | Vernieuwt de gegevens van een tabelwidget. |
| Tabel filteren op de tekst van een veld | Het klassieke interactieve filter. |
| Een widget tonen/verbergen | Wisselt de zichtbaarheid van een widget. |
| Bevestigen en een toast tonen | Vraagt voordat er gehandeld wordt en meldt het aan het eind. |
| Een workflow starten | Zet een proces in gang op het huidige record. |
| Taken bekijken en voltooien | Somt de taken op van wie de app gebruikt en beslist er een. |
| Een signaal naar een workflow sturen | Wekt processen die stonden te wachten. |
| Afmelden | Verlaat de app. |
De ingevoegde code is een vertrekpunt: hij is van u, en is bedoeld om te bewerken. Hij wordt niet opnieuw gegenereerd.
Hoe de code draait
Elke gebeurtenis is een asynchrone functie die maar één ding ontvangt: de
keplin. Daaruit volgen drie praktische gevolgen:
awaitwerkt op het hoogste niveau van de code. U hoeft niets in te pakken.returnverlaat de gebeurtenis. Dat is de normale manier om halverwege af te haken (bijvoorbeeld wanneer een bevestiging geweigerd is).- Er zijn geen parameters. De context zit in
keplinzelf:keplin.eventbrengt de payload mee enkeplin.ctxzegt waar u bent (ctx.widgetis de widget die afging —nullbij gebeurtenissen van het scherm —,ctx.eventis de naam van de gebeurtenis enctx.screenhet scherm).
Zolang de code van een knop niet klaar is, toont de knop drie geanimeerde stippen: wie de app gebruikt merkt dat er gewerkt wordt. Klapt de code, dan breekt het scherm niet: er verschijnt een melding en de fout wordt in Radar vastgelegd, met het scherm, de widget en de gebeurtenis waar het gebeurde.
De SDK keplin
Alles wat de code kan doen zit onder keplin. Dit zijn de gebieden:
| Gebied | Waarvoor |
|---|---|
keplin.event / keplin.ctx |
De payload van de gebeurtenis en de context waarin zij draait. |
keplin.widgets |
Praten met de widgets van het scherm. |
keplin.data |
De datastores: lezen, schrijven, filteren, opslaan. |
keplin.nav |
Navigeren en de routeparameters lezen. |
keplin.ui |
Meldingen, bevestigingen en modale vensters. |
keplin.state |
State die tussen schermen gedeeld wordt. |
keplin.session |
Wie de app gebruikt, en wat die mag doen. |
keplin.auth |
Aanmelden, registreren en wachtwoordherstel (systeemschermen). |
keplin.i18n |
Vertaalde zinnen. |
keplin.storage |
Voorkeuren die op het apparaat bewaard worden. |
keplin.api |
De API's van de app rechtstreeks aanroepen. |
keplin.reports |
Rapporten openen en downloaden. |
keplin.workflow |
Processen starten, taken opsommen en voltooien. |
De widgets
keplin.widgets.get("id") geeft de handle van een widget terug. Alle handles
hebben dezelfde basis:
const w = keplin.widgets.get("w_fic_tel1");
w.show(); // tonen
w.hide(); // verbergen
w.setEnabled(false); // uitschakelen
w.set("label", "Mobiel"); // elke eigenschap uit de inspector wijzigen
w.get("label"); // de werkelijke waarde lezen
w.reset(); // de wijzigingen tijdens uitvoering vergeten
En daarna voegt elke familie toe wat haar eigen is:
| Familie | Wat die toevoegt |
|---|---|
| Formuliervelden | getValue(), setValue(v), validate(), error |
| Gegevenswidgets (Tabel, Lijst, Kaarten, Grafiek, KPI, Kanban, Kalender, Gantt) | rows, total, refresh(), setFilter(where), setSort(sort) |
| Tabel | selectedRow, selectedRows, clearSelection() |
| KPI | value, values, valueOf(indicatorId) |
| Kanban | columns, moveCard(id, kolom, index?) |
| Kalender | view, start, end, goTo(datum), setView(weergave) |
| Gantt | zoom, setZoom(z) |
| Tabbladen | activeTab, tab("id") — en, op het tabblad, activate(), show(), hide(), setEnabled() |
| Label / Knop / Link / Kruimelpad | setText(t) / setLabel(t) |
| Markdown | setContent(md) |
| Externe pagina | setUrl(url), reload() |
| Exporteren | export() |
| Rapport | url, download() |
Nota
De suggesties van de editor bieden alle werkwoorden van alle families,
omdat de editor niet van tevoren weet welke widget dat id is. Tijdens de
uitvoering bestaan alleen die van het echte type — moveCard op een Knop doet
niets nuttigs.
De gegevens
keplin.data.store("naam") geeft een datastore van het scherm terug op naam (zie
Datastores en gegevens).
In een record-datastore:
const conta = keplin.data.store("conta");
conta.get("nome"); // een veld lezen
conta.set("estado", "ativo"); // een veld schrijven (nog niet opgeslagen)
conta.record(); // het hele record
conta.isDirty(); // zijn er wijzigingen die nog niet opgeslagen zijn?
conta.reset(); // de wijzigingen weggooien
const ok = await conta.save(); // valideert en slaat op; true als er opgeslagen is
In een lijst-datastore:
const contas = keplin.data.store("contas");
contas.rows(); // de geladen rijen
contas.total(); // het totaal (wanneer de server het geeft)
contas.reload(); // opnieuw lezen
contas.setWhere({ estado: { eq: "ativo" } }); // extra filter; null wist het
contas.setSort([{ field: "nome", direction: "ASC" }]);
contas.goToPage(2);
In beide zegt status() hoe het laden ervoor staat (idle, loading, ready,
error).
Navigatie
keplin.nav.go("/ficha-de-conta/17"); // naar een route gaan (met parameters)
keplin.nav.back(); // teruggaan
keplin.nav.params; // de parameters van het huidige scherm, op naam
Meldingen, bevestigingen en modale vensters
keplin.ui.toast("Opgeslagen.", "success"); // "success" | "error" | "info"
const ok = await keplin.ui.confirm("Het record verwijderen?");
if (!ok) return;
De bevestiging is een venster met het thema van de app — nooit het grijze kadertje van de browser.
State, sessie en voorkeuren
keplin.state.set("filtroContas", "activas"); // leeft zolang het tabblad open is
keplin.state.get("filtroContas");
keplin.state.remove("filtroContas");
keplin.session.user; // { id, username, name } — null op openbare schermen
keplin.session.roles; // de rollen van wie de app gebruikt
keplin.session.can("contas.editar"); // heeft die deze actie? (Instellingen ▸ Rechten)
await keplin.session.logout();
keplin.storage.set("colunasContas", ["nome", "cidade"]); // blijft op het apparaat
keplin.storage.get("colunasContas");
Dica
Vraag keplin.session.can("...") om te beslissen wat iemand mag doen, en niet
hasRole("gestor"). De acties worden gedeclareerd bij Instellingen ▸
Rechten en overleven herindelingen van rollen; de naam van een rol niet.
De API's en de rapporten
const linhas = await keplin.api.query("contas", { estado: "ativo" }, ["id", "nome"]);
await keplin.api.mutate("criarConta", { nome: "Nova" }, ["id"]);
keplin.reports.open("Contactos da conta", { contaId: 17 });
keplin.reports.download("Contactos da conta", { contaId: 17 }, "xlsx");
Bij een query is de lijst met velden verplicht — zij is het die zegt wat u wilt ophalen.
Atenção
keplin.reports.open opent een nieuw tabblad en mag daarom niet achter een
await staan: buiten het gebaar van de gebruiker blokkeert de browser het
venster. Open eerst, doe de rest daarna.
Workflows
const registo = keplin.data.store("oportunidade").get("id");
await keplin.workflow.start("wf_aprovacao", registo);
const tarefas = await keplin.workflow.tasks();
await keplin.workflow.complete(tarefas[0].id, "aprovar");
const { woken } = await keplin.workflow.signal("documento-recebido", registo);
Vertaalde zinnen
keplin.i18n.t("{n} contas activas", { n: linhas.length });
keplin.i18n.locale;
Modale schermen
Een scherm van Keplin is niet modaal omdat het op een bepaalde manier geopend is — het is modaal omdat het zo is ingesteld. De beslissing zit in de inspector van het scherm, in de categorie Presentatie:

| Optie | Wat die doet |
|---|---|
| Modus | Scherm (een gewone pagina), Modaal venster (gecentreerd) of Zijpaneel (rechts). |
| Breedte (px) / Hoogte (px) | Het formaat van het modale venster. Het zijpaneel gebruikt de volle hoogte. |
| Sluitknop | Toont de × in de hoek. |
| Klik ernaast sluit / Esc sluit | De twee gebruikelijke uitgangen. |
| Het scherm erachter vernieuwen bij sluiten | Bij het sluiten lezen de datastores van het aanroepende scherm opnieuw. |
De tip van de sectie zelf vat het samen: Opent BOVEN OP het scherm dat het
aanroept (Link, gebeurtenissen of keplin.ui.openModal). Valt buiten de directe
navigatie.
Openen en sluiten met code
const resultado = await keplin.ui.openModal("/nova-conta", { setor: "banca" });
if (resultado) {
keplin.data.store("contas").reload();
}
openModalontvangt de route (of het id) van het scherm en, eventueel, de parameters.- De belofte lost pas op wanneer het modale venster sluit, en brengt de waarde mee die het venster teruggaf.
- Binnen het modale venster sluit
keplin.ui.closeModal(waarde)het en geeft die waarde terug. - Modale vensters stapelen: een modaal venster mag een ander openen.
Nota
keplin.nav.go("/route") naar een scherm dat als Modaal venster
(gecentreerd) of Zijpaneel (rechts) is ingesteld, opent het als modaal
venster in plaats van ernaartoe te navigeren. Dat is met opzet: een modaal
scherm heeft geen eigen adres in de navigatie.
Recepten
Opslaan en teruggaan (de onClick van de knop Opslaan van Ficha de Conta):
const ok = await keplin.data.store("conta").save();
if (ok) {
keplin.ui.toast("Account opgeslagen");
keplin.nav.go("/contas");
}
De kaart van de aangeklikte rij openen (onRowClick van een Tabel):
keplin.nav.go(`/ficha-de-conta/${keplin.event.row["id"]}`);
Een tabel filteren op een tekstvak (onChange van het tekstvak — vervang de
id's door die van uw scherm):
const texto = keplin.widgets.get("w_pesquisa").getValue();
keplin.widgets.get("w_cta_tab1").setFilter(texto ? { nome: { contains: texto } } : null);
Bevestigen vóór een destructieve actie (onClick van een knop):
if (!(await keplin.ui.confirm("Deze account verwijderen?"))) return;
Een knop verbergen voor wie niet mag (onLoad van het scherm):
if (!keplin.session.can("contas.eliminar")) {
keplin.widgets.get("w_apagar").hide();
}
Waarom niet…?
- Waarom laat hij mij de gebeurtenis niet opslaan? De code compileert niet. De boodschap is De gebeurtenis is niet opgeslagen: de code is niet uitvoerbaar — herstel het en sla op.
- Waarom geeft
keplin.widgets.get("...")een fout? Het id bestaat niet op dit scherm. Controleer het bovenaan de inspector, met de widget geselecteerd; en denk eraan dat elk apparaat een eigen boom is (zie Lay-outs en ontwerp per apparaat). - Waarom ging
onParamsChangeniet af bij het openen? Dat is met opzet: hij gaat alleen af bij wijzigingen. Gebruik voor het opstartenonLoad. - Waarom geeft het modale venster niets terug? Ofwel bestaat het doelscherm niet, ofwel heeft wie de app gebruikt geen recht om het te openen — in beide gevallen lost de belofte op zonder waarde. Controleer de route en de rechten.
- Waarom opent het venster van het rapport niet? U hebt de
openachter eenawaitgezet. Open eerst. - Waarom lijkt mijn gebeurtenis niet te draaien? Kijk in Radar: de fouten van de code van de gebeurtenissen belanden daar, met scherm, widget en gebeurtenis — en Radar brengt u rechtstreeks naar de editor van die gebeurtenis.