Eventi e SDK
Gli eventi dei widget e della schermata, l'editor di codice, le azioni predefinite e l'SDK keplin in TypeScript — dati, widget, navigazione, sessione, modali e workflow.
C'è tanta schermata che si fa senza scrivere una riga: collegare un datastore, trascinare widget, puntare un pulsante a un'altra schermata. Ma prima o poi arriva il "quando succede questo, fai quello" — salvare e tornare indietro, ricaricare una tabella dopo un filtro, confermare prima di eliminare, aprire un modale e usare ciò che ha restituito.
È a questo che servono gli eventi: punti della schermata dove gira codice tuo,
scritto in TypeScript, con un SDK — l'oggetto keplin — che dà accesso a
tutto ciò che la schermata ha.
Dove sono gli eventi
Nell'inspector, l'ultima categoria di un widget (e della schermata stessa) si chiama Eventi. Ha una riga per ogni evento disponibile, e in ogni riga:
- un punto a sinistra: pieno quando quell'evento ha già del codice, vuoto quando non ne ha;
- un pulsante … a destra, che apre l'editor.

I nomi degli eventi non sono tradotti — sono gli stessi in qualsiasi lingua
(onClick, onRowClick, onLoad), perché sono anche i nomi che appaiono
nel codice e nei registri del Radar.
Gli eventi della schermata
Clicca su una zona vuota del canvas perché l'inspector mostri la schermata. La categoria Eventi ne ha tre:
| Evento | Quando scatta | Per che cosa |
|---|---|---|
onLoad |
Una volta, quando la schermata si apre. | Preparare lo stato, caricare cose che i datastore non caricano, dare il benvenuto. |
onParamsChange |
Ogni volta che i parametri del percorso cambiano — e non alla prima apertura. | Reagire a un cambio di record senza riaprire la schermata. |
onUnload |
Quando la schermata se ne va. | Pulire lo stato, salvare bozze. |

Gli eventi di ogni widget
Ogni tipo di widget dichiara i suoi. Oltre al nome, conta il payload —
i dati che l'evento porta con sé, e che il codice legge in keplin.event.
Campi di modulo
| Widget | Eventi | keplin.event |
|---|---|---|
| Casella di testo, Area di testo, Numero, Sì/No, Dropdown, Data, Colore | onChange |
{ value } |
| File | onChange, onUpload |
onUpload: { file, name } |
Azioni e navigazione
| Widget | Eventi | keplin.event |
|---|---|---|
| Pulsante | onClick |
{} |
| Pulsante con menu | onClick, onMenuItem |
onMenuItem: { id, label } |
| Link | onClick |
{} |
| Esporta | onExport, onDataLoaded |
onExport: { rows, filename } |
Struttura e contenuto
| Widget | Eventi | keplin.event |
|---|---|---|
| Schede | onTabChange |
{ tab } |
| Report | onLoad |
{ report } |
Widget di dati
| Widget | Eventi | keplin.event |
|---|---|---|
| Tabella | onRowClick, onRowDoubleClick, onSelectionChange, onDataLoaded |
{ row, index } · onSelectionChange: { row, rows } |
| Elenco, Card | onRowClick, onDataLoaded |
{ row, index } |
| Grafico | onClick |
{ name, seriesName, value, dataIndex } |
| KPI | onClick, onDataLoaded |
{ value, indicatorId } |
Board e pianificazione
| Widget | Eventi | keplin.event |
|---|---|---|
| Kanban | onCardClick, onCardCreate, onCardMoved, onDataLoaded |
{ row } · { column } · { row, from, to, index } |
| Calendario | onEventClick, onDayClick, onRangeSelect, onRangeChange, onDataLoaded |
{ row } · { date } · { start, end } · { start, end, view } |
| Gantt | onBarClick, onEmptyClick, onDataLoaded |
{ row } · { date } |
Processi
| Widget | Eventi | keplin.event |
|---|---|---|
| Stato del processo | onDecide |
{ task, outcome } |
| Le mie attività | onOpen, onDecide |
{ task, screenId } |
Nota
I widget programmati da te (quelli che appaiono nella palette in Custom) dichiarano i propri eventi, e appaiono qui come tutti gli altri.
L'editor di codice
Il pulsante … di un evento apre l'editor in un modale. Il titolo dice dove sei:
l'id del widget (o il nome della schermata) e il nome dell'evento — w_fic_sav1 · onClick.

| Pulsante | Che cosa fa |
|---|---|
| Inserisci azione | Scrive per te il codice di un'attività comune (qui sotto). |
| Rimuovi handler | Elimina il codice di questo evento. Il punto torna vuoto. |
| Annulla | Chiude senza salvare. |
| Salva | Verifica e salva. |
L'editor ha suggerimenti mentre scrivi (Ctrl+Spazio): tutto il keplin
è dichiarato, con i tipi giusti — e, meglio ancora, gli id dei
widget di questa schermata sono lì dentro. Scrivere keplin.widgets.get(" mostra l'
elenco dei widget della schermata, e un id che non esiste viene segnalato come errore prima
che tu salvi.
Atenção
Al salvataggio, il codice viene compilato. Se non è eseguibile, la piattaforma lo rifiuta — L'evento non è stato salvato: il codice non è eseguibile — e il modale resta aperto perché tu corregga. Una schermata non resta mai con codice rotto lì dentro.
Le azioni predefinite
Inserisci azione apre un elenco delle attività più comuni. Ne scegli una e il codice viene scritto in fondo a ciò che c'è già, già con i nomi reali della tua schermata — il primo datastore di record, la prima tabella, la prima casella di testo.

| Azione | Che cosa scrive |
|---|---|
| Salva datastore | Valida e salva il record, con avviso di successo. |
| Ricarica datastore | Rilegge i dati di un datastore. |
| Filtra datastore (per testo) | Legge il testo di una casella e lo applica come filtro. |
| Naviga verso una schermata | Salta a un altro percorso dell'app. |
| Ricarica una tabella | Aggiorna i dati di un widget di tabella. |
| Filtra la tabella con il testo di un campo | Il filtro interattivo classico. |
| Mostra/nascondi un widget | Alterna la visibilità di un widget. |
| Conferma e mostra un toast | Chiede prima di agire e avvisa alla fine. |
| Avvia un workflow | Mette in moto un processo sul record attuale. |
| Visualizza e completa le attività | Elenca le attività di chi sta usando l'app e ne decide una. |
| Invia un segnale a un workflow | Sveglia i processi che erano in attesa. |
| Esci | Esce dall'app. |
Il codice inserito è un punto di partenza: diventa tuo, ed è lì per essere modificato. Non viene generato di nuovo.
Come gira il codice
Ogni evento è una funzione asincrona che riceve una cosa sola: il keplin.
Da lì escono tre conseguenze pratiche:
awaitfunziona in cima al codice. Non serve avvolgere nulla.returnesce dall'evento. È il modo normale di rinunciare a metà (per esempio, quando una conferma è stata rifiutata).- Non ci sono parametri. Il contesto arriva dentro il
keplinstesso:keplin.eventporta il payload ekeplin.ctxdice dove sei (ctx.widgetè il widget che ha fatto scattare l'evento —nullnegli eventi di schermata —,ctx.eventè il nome dell'evento ectx.screenla schermata).
Finché il codice di un pulsante non finisce, il pulsante mostra tre puntini animati: chi sta usando l'app capisce che sta lavorando. Se il codice esplode, la schermata non si rompe: appare un avviso e l'errore resta registrato nel Radar, con la schermata, il widget e l'evento in cui è successo.
L'SDK keplin
Tutto ciò che il codice può fare sta sotto keplin. Queste sono le aree:
| Area | Per che cosa |
|---|---|
keplin.event / keplin.ctx |
Il payload dell'evento e il contesto in cui sta girando. |
keplin.widgets |
Parlare con i widget della schermata. |
keplin.data |
I datastore: leggere, scrivere, filtrare, salvare. |
keplin.nav |
Navigare e leggere i parametri del percorso. |
keplin.ui |
Avvisi, conferme e modali. |
keplin.state |
Stato condiviso fra schermate. |
keplin.session |
Chi sta usando l'app, e che cosa può fare. |
keplin.auth |
Login, registrazione e recupero della password (schermate di sistema). |
keplin.i18n |
Frasi tradotte. |
keplin.storage |
Preferenze salvate sul dispositivo. |
keplin.api |
Chiamare le API dell'app direttamente. |
keplin.reports |
Aprire e scaricare report. |
keplin.workflow |
Avviare processi, elencare e completare attività. |
I widget
keplin.widgets.get("id") restituisce l'handle di un widget. Tutti gli handle
hanno la stessa base:
const w = keplin.widgets.get("w_fic_tel1");
w.show(); // mostrare
w.hide(); // nascondere
w.setEnabled(false); // disattivare
w.set("label", "Cellulare"); // cambiare qualsiasi proprietà dell'inspector
w.get("label"); // leggere il valore effettivo
w.reset(); // dimenticare le modifiche fatte in esecuzione
E poi ogni famiglia aggiunge ciò che le è proprio:
| Famiglia | Che cosa aggiunge |
|---|---|
| Campi di modulo | getValue(), setValue(v), validate(), error |
| Widget di dati (Tabella, Elenco, Card, Grafico, KPI, Kanban, Calendario, Gantt) | rows, total, refresh(), setFilter(where), setSort(sort) |
| Tabella | selectedRow, selectedRows, clearSelection() |
| KPI | value, values, valueOf(indicadorId) |
| Kanban | columns, moveCard(id, coluna, índice?) |
| Calendario | view, start, end, goTo(data), setView(vista) |
| Gantt | zoom, setZoom(z) |
| Schede | activeTab, tab("id") — e, sulla scheda, activate(), show(), hide(), setEnabled() |
| Etichetta / Pulsante / Link / Breadcrumb | setText(t) / setLabel(t) |
| Markdown | setContent(md) |
| Pagina esterna | setUrl(url), reload() |
| Esporta | export() |
| Report | url, download() |
Nota
I suggerimenti dell'editor offrono tutti i verbi di tutte le famiglie,
perché l'editor non sa in anticipo che widget sia quell'id. In esecuzione esistono
solo quelli del tipo reale — moveCard su un Pulsante non fa nulla di utile.
I dati
keplin.data.store("nome") restituisce un datastore della schermata per nome (vedi
Datastore e dati).
In un datastore di record:
const conta = keplin.data.store("conta");
conta.get("nome"); // leggere un campo
conta.set("estado", "ativo"); // scrivere un campo (resta da salvare)
conta.record(); // il record intero
conta.isDirty(); // ci sono modifiche da salvare?
conta.reset(); // buttare via le modifiche
const ok = await conta.save(); // valida e salva; true se ha salvato
In un datastore di elenco:
const contas = keplin.data.store("contas");
contas.rows(); // le righe caricate
contas.total(); // il totale (quando il server lo dà)
contas.reload(); // rileggere
contas.setWhere({ estado: { eq: "ativo" } }); // filtro extra; null pulisce
contas.setSort([{ field: "nome", direction: "ASC" }]);
contas.goToPage(2);
In entrambi, status() dice a che punto è il caricamento (idle, loading,
ready, error).
Navigazione
keplin.nav.go("/ficha-de-conta/17"); // andare a un percorso (con parametri)
keplin.nav.back(); // tornare indietro
keplin.nav.params; // i parametri della schermata attuale, per nome
Avvisi, conferme e modali
keplin.ui.toast("Salvato.", "success"); // "success" | "error" | "info"
const ok = await keplin.ui.confirm("Eliminare il record?");
if (!ok) return;
La conferma è una finestra di dialogo con il tema dell'app — mai la scatola grigia del browser.
Stato, sessione e preferenze
keplin.state.set("filtroContas", "activas"); // vive finché la scheda resta aperta
keplin.state.get("filtroContas");
keplin.state.remove("filtroContas");
keplin.session.user; // { id, username, name } — null nelle schermate pubbliche
keplin.session.roles; // i ruoli di chi sta usando l'app
keplin.session.can("contas.editar"); // ha questa azione? (Impostazioni ▸ Autorizzazioni)
await keplin.session.logout();
keplin.storage.set("colunasContas", ["nome", "cidade"]); // resta sul dispositivo
keplin.storage.get("colunasContas");
Dica
Per decidere che cosa qualcuno può fare, chiedi keplin.session.can("...") e
non hasRole("gestor"). Le azioni si dichiarano in Impostazioni ▸
Autorizzazioni e sopravvivono alle riorganizzazioni dei ruoli; il nome di un ruolo,
no.
Le API e i report
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");
In una query, l'elenco dei campi è obbligatorio — è lui a dire che cosa vuoi portare.
Atenção
keplin.reports.open apre una scheda nuova e perciò non può stare
dietro a un await: fuori dal gesto dell'utente, il browser blocca la
finestra. Apri prima, fai il resto dopo.
Workflow
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);
Frasi tradotte
keplin.i18n.t("{n} account attivi", { n: linhas.length });
keplin.i18n.locale;
Schermate modali
Una schermata di Keplin non è modale perché è stata aperta in un certo modo — è modale perché è stata configurata così. La decisione sta nell'inspector della schermata, nella categoria Presentazione:

| Opzione | Che cosa fa |
|---|---|
| Modalità | Schermata (una pagina normale), Modale (al centro) o Pannello laterale (a destra). |
| Larghezza (px) / Altezza (px) | La dimensione del modale. Il pannello laterale usa tutta l'altezza. |
| Pulsante di chiusura | Mostra la × nell'angolo. |
| Clic fuori chiude / Esc chiude | Le due uscite abituali. |
| Aggiorna la schermata dietro alla chiusura | Alla chiusura, i datastore della schermata che l'ha chiamata rileggono. |
Il suggerimento della sezione stessa riassume: Si apre SOPRA la schermata che lo chiama (Link,
eventi o keplin.ui.openModal). Escluso dalla navigazione diretta.
Aprire e chiudere da codice
const resultado = await keplin.ui.openModal("/nova-conta", { setor: "banca" });
if (resultado) {
keplin.data.store("contas").reload();
}
openModalriceve il percorso (o l'id) della schermata e, facoltativamente, i parametri.- La promise si risolve solo quando il modale si chiude, e porta il valore che il modale ha restituito.
- Dentro il modale,
keplin.ui.closeModal(valor)chiude e restituisce quel valore. - I modali si impilano: un modale può aprirne un altro.
Nota
keplin.nav.go("/rota") verso una schermata configurata come Modale (al centro) o
Pannello laterale (a destra) la apre come modale invece di navigare. È
voluto: una schermata modale non ha un indirizzo proprio nella navigazione.
Ricette
Salvare e tornare indietro (l'onClick del pulsante Salva della Scheda Account):
const ok = await keplin.data.store("conta").save();
if (ok) {
keplin.ui.toast("Account salvato");
keplin.nav.go("/contas");
}
Aprire la scheda della riga cliccata (onRowClick di una Tabella):
keplin.nav.go(`/ficha-de-conta/${keplin.event.row["id"]}`);
Filtrare una tabella con una casella di testo (onChange della casella —
sostituisci gli id con quelli della tua schermata):
const texto = keplin.widgets.get("w_pesquisa").getValue();
keplin.widgets.get("w_cta_tab1").setFilter(texto ? { nome: { contains: texto } } : null);
Confermare prima di un'azione distruttiva (onClick di un pulsante):
if (!(await keplin.ui.confirm("Eliminare questo account?"))) return;
Nascondere un pulsante a chi non può (onLoad della schermata):
if (!keplin.session.can("contas.eliminar")) {
keplin.widgets.get("w_apagar").hide();
}
Perché no…?
- Perché non mi lascia salvare l'evento? Il codice non compila. Il messaggio è L'evento non è stato salvato: il codice non è eseguibile — correggi e salva.
- Perché
keplin.widgets.get("...")dà errore? L'id non esiste in questa schermata. Verificalo in cima all'inspector, con il widget selezionato; e ricordati che ogni dispositivo è un albero proprio (vedi Layout e design per dispositivo). - Perché l'
onParamsChangenon è scattato all'apertura? È voluto: scatta solo sui cambiamenti. Per l'avvio, usa l'onLoad. - Perché il modale non restituisce nulla? O la schermata di destinazione non esiste, oppure chi sta usando l'app non ha il permesso di aprirla — in entrambi i casi la promise si risolve senza valore. Verifica il percorso e i permessi.
- Perché non apre la finestra del report? Hai messo l'
opendopo unawait. Apri prima. - Perché il mio evento sembra non girare? Guarda il Radar: gli errori del codice degli eventi restano lì, con schermata, widget ed evento — e il Radar ti porta direttamente all'editor di quell'evento.