Widget personalizzati

Prev Next

Crea i tuoi widget in Tulip utilizzando HTML, CSS e JavaScript

Who can use this feature

Users on Professional plans and above.

Requirement to use Custom Widget

Please allow Outgoing access to Custom Widgets. See Networking requirements for a Tulip Cloud Deployment to learn more.* *.tulip-custom-widgets.com

Gli elementi che trascini nella tua app sono chiamati widget. Possono essere pulsanti, immagini, caselle di immissione dati e qualsiasi altro elemento disponibile nella barra degli strumenti.

I widget personalizzati ti consentono di scrivere il tuo codice e creare i tuoi widget, che potrai poi trascinare e rilasciare nella tua applicazione e con cui potrai interagire. Questa funzionalità permette agli utenti di Tulip di ampliare i confini di ciò che è possibile realizzare in Tulip.

Guarda il seguente video per una panoramica di questa funzionalità:

Scopri i widget personalizzati disponibili nella Libreria di Tulip.

Nozioni di base sui widget personalizzati

La schermata di gestione dei widget personalizzati si trova nella pagina delle impostazioni del tuo account. Da qui puoi creare ed eliminare i widget. L’editor dei widget personalizzati ti permette di scrivere il codice del tuo widget, creare Props ed Eventi e visualizzare un’anteprima del tuo widget. Di seguito è riportato un diagramma che descrive cosa sono i props e gli eventi:

Props: i props sono dati condivisi tra i widget personalizzati e le applicazioni Tulip. I props saranno visibili nell’editor dell’applicazione e consentiranno agli editori di app di selezionare quali variabili, record di tabella o altre informazioni associare al prop.

Eventi: gli eventi sono segnali che il tuo widget può inviare alla tua applicazione. Gli eventi consentono alla tua applicazione di rispondere con un trigger e possono trasportare informazioni al loro interno.

È possibile accedere ai widget personalizzati tramite il menu a tendina “Personalizzato” nell’editor dell’app.

Creazione di un widget

Permission Required

Only users with access to account settings (i.e. Account Owners) or users with specific permission to access the custom widget page can use the custom widget editor.

La schermata dell’editor del widget è suddivisa in quattro sezioni. Il codice scritto nella sezione in basso a sinistra apparirà nella sezione di anteprima una volta cliccato su di essa. I valori delle proprietà possono essere modificati direttamente nella sezione di anteprima a scopo di test.

Sono disponibili funzioni speciali per interagire con le proprietà (props) e gli eventi.

Ottenere il valore di una proprietà

Warning

It is not guaranteed that the prop being used by a widget will load before that widget loads, so all props associated with Table record fields, Aggregations, or any other dynamic value should include logic to support cases where they are null at the point the widget is loaded. See this section for details on triggering logic when a prop changes.

//Ottenere il valore di una proprietà getValue('My Prop');``//Assegnare la proprietà a una variabile let myVariable = getValue('My Prop');

Eseguire un'azione quando una proprietà cambia

//Eseguire un'azione ogni volta che il valore di un prop cambia getValue('My Prop', (internalVariable) => { doSomething(internalVariable);});

Impostare il valore di un prop


//Imposta il valore di una proprietà di tipo testo su 'hello!' setValue('My Text Prop', 'hello!');

// Imposta il valore di un elemento all’interno di un oggetto setValue('My Object Prop', { 'Key inside object': 'new value' });```

### Generare un evento

```**Attiva un evento** fireEvent('evento', *payload*);

// Generare un evento senza payload fireEvent('My Event');

//Attiva un evento con payload fireEvent('My Event', myVariable);```

Il diagramma seguente illustra il flusso di informazioni attraverso un widget personalizzato in un'app. I dati della variabile di input vengono memorizzati nella proprietà. La modifica del valore segnala un evento in cui un trigger memorizza quindi i dati nella variabile di output.

![Custom Widget App Setup Diagram](https://cdn.document360.io/7c6ff534-cad3-4fc8-9583-912c4016362f/Images/Documentation/Custom%20Widget%20App%20Setup%20Diagram.jpg){height="" width=""}

## Demo di creazione del widget

![Video](https://www.youtube.com/embed/H08-W2M2Ypo?rel=0){height="576" width="1024"}

## Importazione/esportazione di app

Consulta la sezione [Importazione ed esportazione](/r230/docs/import-export) per ulteriori informazioni su questa funzionalità.

### Esporta

1. Dalla panoramica dei widget personalizzati, seleziona il **menu con i tre puntini** accanto al widget che desideri esportare.

![](https://cdn.document360.io/7c6ff534-cad3-4fc8-9583-912c4016362f/Images/Documentation/Custom%20Widgets%20Overview_477703991.png){height="" width=""}

1. Seleziona " **Esporta**".

![](https://cdn.document360.io/7c6ff534-cad3-4fc8-9583-912c4016362f/Images/Documentation/Custom%20Widgets%20Overview_477704053.png){height="" width="800"}

### Importa

1. Dalla schermata di panoramica dei widget personalizzati, seleziona il **menu con i tre puntini** in alto.

![](https://cdn.document360.io/7c6ff534-cad3-4fc8-9583-912c4016362f/Images/Documentation/Custom%20Widgets%20Overview_477704175.png){height="" width="800"}

1. Seleziona " **Importa**".

![](https://cdn.document360.io/7c6ff534-cad3-4fc8-9583-912c4016362f/Images/Documentation/Custom%20Widgets%20Overview_477704228.png){height="" width="350"}

1. Individua il file .json del widget personalizzato.

## Abilita le librerie esterne

Le librerie esterne ampliano ulteriormente le funzionalità dei widget personalizzati. Le librerie esterne gestiscono il lavoro manuale di scrittura dell'HTML direttamente in JavaScript. Le librerie esterne devono essere abilitate per ogni widget in cui si desidera utilizzarle.

1. All'interno del widget, fare clic sul **menu con i tre puntini** in alto a destra della pagina.

![](https://cdn.document360.io/7c6ff534-cad3-4fc8-9583-912c4016362f/Images/Documentation/Custom%20Widgets%20Overview_477684182.png){height="" width=""}

1. Seleziona **Abilita libreria esterna**.

![](https://cdn.document360.io/7c6ff534-cad3-4fc8-9583-912c4016362f/Images/Documentation/Custom%20Widgets%20Overview_477684247.png){height="" width="250"}

1. Seleziona **l’estensione** che desideri abilitare.

![Screenshot 2025-02-06 at 9.13.14 AM](https://cdn.document360.io/7c6ff534-cad3-4fc8-9583-912c4016362f/Images/Documentation/Screenshot%202025-02-06%20at%209.13.14%20AM.png){height="" width=""}

Ecco una descrizione di base delle funzionalità di ciascuna estensione:

* **jQuery** - consente una selezione più snella degli elementi HTML, oltre alla manipolazione degli stessi.
* **D3 -** Il punto di riferimento per la visualizzazione dei dati: una curva di apprendimento ripida, ma un'enorme flessibilità.
* **Google Visualization -** Un ottimo strumento per la visualizzazione, proprio come suggerisce il nome.
* **Lodash -** Lodash offre tantissimi strumenti che semplificano il lavoro con i tipi di dati JavaScript.
* **ChartJS -** Probabilmente la libreria per la creazione di grafici più semplice in assoluto, con tantissime risorse online a disposizione.
* **Moment -** Moment offre tantissimi strumenti per lavorare con date e orari.

## Versione del runtime

Il modo in cui i widget personalizzati vengono caricati nel nuovo runtime è stato aggiornato (vedi [Miglioramenti ai widget personalizzati](/r230/docs/custom-widget-improvements)). Questa modifica influisce sul momento in cui è possibile accedere in modo affidabile alle proprietà del widget, come quelle recuperate con `getValue()`.

Per verificare quale versione del runtime utilizza il proprio widget:

* Nella pagina **dell’elenco dei widget personalizzati** o nell’editor, se accanto al nome del widget è presente un badge **“Legacy”**, il widget utilizza il runtime precedente. Se il badge non è presente, il widget utilizza il nuovo runtime.
* Nel pannello laterale delle informazioni sul widget personalizzato, consultare la sezione **Runtime**.
* Nelle **impostazioni dei permessi** di un widget personalizzato, se l’interruttore " **Runtime del widget personalizzato** " è attivato, il widget utilizza il nuovo runtime.

:::(Info) (NOTE)
By default, newly created Custom Widgets use the old runtime.
:::

Per cambiare la versione del runtime di un widget personalizzato, usa l’interruttore " **Runtime del widget personalizzato** " nelle **impostazioni dei permessi**.![Custom widget runtime toggle](https://cdn.document360.io/7c6ff534-cad3-4fc8-9583-912c4016362f/Images/Documentation/Custom%20widget%20runtime%20toggle.png){height="" width=""}

## Esempi

Di seguito sono riportati diversi widget personalizzati della [Libreria Tulip](https://tulip.co/library/) che illustrano le funzionalità descritte sopra:

### Widget timer semplice

[Widget timer semplice](https://library.tulip.co/apps/simple-timer-widget)

Un widget timer intuitivo per monitorare il tempo trascorso durante i flussi di lavoro dell’app. Gli operatori possono avviare, arrestare e azzerare il timer direttamente dall’interfaccia dell’app, rendendolo utile per cronometrare attività o processi.

![Screenshot 2025-07-02 at 15.33.20.png](https://cdn.document360.io/7c6ff534-cad3-4fc8-9583-912c4016362f/Images/Documentation/Screenshot%202025-07-02%20at%2015.33.20.png){height="" width=""}

### Widget indicatore radiale

[Widget indicatore radiale](https://library.tulip.co/apps/radial-gauge-widget)

Questo widget rappresenta visivamente i valori numerici tramite un indicatore radiale personalizzabile. È ideale per visualizzare metriche quali la velocità di una macchina, la temperatura o lo stato di avanzamento in un formato rapido e di facile comprensione.

![Screenshot 2025-07-02 at 15.34.22.png](https://cdn.document360.io/7c6ff534-cad3-4fc8-9583-912c4016362f/Images/Documentation/Screenshot%202025-07-02%20at%2015.34.22.png){height="" width=""}

### Widget Timeline

[Widget Timeline](https://library.tulip.co/apps/timeline-widget)

Mostra eventi o fasi lungo una linea temporale orizzontale, aiutando gli utenti a visualizzare a colpo d’occhio le fasi di un processo, le attività completate o i dati relativi al tempo.

![Screenshot 2025-07-02 at 15.34.59.png](https://cdn.document360.io/7c6ff534-cad3-4fc8-9583-912c4016362f/Images/Documentation/Screenshot%202025-07-02%20at%2015.34.59.png){height="" width=""}

## Migliori pratiche

### 1. Evita effetti collaterali indesiderati causati dal codice del widget che viene eseguito al momento del caricamento

Tulip esegue il codice effettivo del widget ogni volta che viene visualizzato, non solo nel Player, ma anche nell'editor dei widget personalizzati, nell'editor delle app e nell'anteprima live di una stazione. Ciò consente di ottenere un'anteprima accurata e in tempo reale del widget. Qualsiasi codice che produca effetti collaterali (ad es. chiamate API, scritture in tabelle, notifiche) al livello superiore dello script viene eseguito ogni volta che il widget viene visualizzato, incluse le volte in cui il widget si riapre nell’editor o l’anteprima viene ricaricata.

Fai in modo che qualsiasi azione con un effetto nel mondo reale sia innescata da un trigger esplicito (ad es. la pressione di un pulsante, un `` `fireEvent` `` in risposta a un'azione dell'utente o una modifica di una proprietà) anziché attivarla automaticamente al caricamento.

```// CORRETTO: viene eseguito solo quando l'app lo richiede esplicitamente getValue('TriggerIncrement', (shouldIncrement) => { if (shouldIncrement) { fetch("https://api.example.com/increment-counter", { method: "POST" }); }});

// SBAGLIATO: si attiva ogni volta che il widget viene caricato, anche nell’editor e nell’anteprimafetch("https://api.example.com/increment-counter", { method: "POST" });```Se un widget deve caricare dati automaticamente, usa un endpoint di sola lettura o idempotente in modo che i caricamenti ripetuti non causino effetti indesiderati.

### 2. Eseguire test iterativi senza innescare effetti collaterali reali

Durante lo sviluppo di un widget che chiama un'API di produzione con effetti collaterali reali, punta a un endpoint di test o di staging fino al completamento delle iterazioni, quindi passa a un endpoint di produzione prima della pubblicazione. Ciò impedisce la duplicazione di record, notifiche o altri effetti collaterali derivanti dalla modifica e dall’anteprima del widget.

Per le azioni che devono avvenire in produzione anche dopo la pubblicazione, integra la conferma nell’interfaccia utente del widget stesso, anziché fare affidamento sui tempi di caricamento. Richiedendo un’azione visibile (ad esempio, il clic su un pulsante), chi visualizza il widget ha il controllo su quando si verifica l’effetto.

```// Richiede un clic esplicito prima che l’azione effettiva venga attivata,// indipendentemente da quante volte il widget stesso sia stato renderizzatodocument.getElementById('submitBtn')?.addEventListener('click', () => { fetch("https://api.example.com/submit-order", { method: "POST" });});

3. Gestione delle proprietà (prop) - Verifica il valore delle proprietà

Assicurati che il tuo widget personalizzato gestisca i dati in modo affidabile. Le proprietà (props) sono un modo per passare informazioni al tuo widget, ma i loro valori possono cambiare o potrebbero essere inizialmente indefiniti. Anche se le proprietà hanno un tipo prestabilito, potrebbero inizialmente essere indefinite e ciò potrebbe causare errori nel codice del tuo widget personalizzato. Seguire un buon modello previene gli errori e rende il tuo widget più robusto.

L’esempio riportato di seguito utilizza una funzione di callback come secondo parametro per il metodo getValue. Questa funzione di callback verrà eseguita ogni volta che il widget personalizzato riceve il valore aggiornato della proprietà.

Poiché il valore iniziale della prop potrebbe essere indefinito e in seguito potrebbe ricevere il valore aggiornato, è necessario assicurarsi di applicare la configurazione una volta ricevuto il valore aggiornato. L’uso di getValue senza la funzione di callback restituisce il valore della prop in quel punto dell’esecuzione del codice.

getValue('config', function(config) { if (config) { applyConfiguration(config); }});

L’esempio seguente non è consigliato poiché il valore iniziale della proprietà potrebbe essere indefinito:const config = getValue('config');applyConfiguration(config);

4. Gestione degli eventi - Convalida dei dati

I dati degli eventi hanno un tipo. Sebbene l’attivazione di un evento con un tipo di dati errato non causi un crash né interrompa l’esecuzione del codice del widget personalizzato, è buona norma disporre di controlli propri per garantire che i dati inviati tramite fireEvent siano convalidati e di tipo corretto.

Questa pratica previene comportamenti imprevisti e rende il codice più prevedibile.

if (data != null && typeof data === 'string') { fireEvent(eventName, data);} else { // Gestire facoltativamente il caso di dati non validi.}

5. Utilizza una funzione di callback per l’accesso asincrono alle proprietà

Quando chiami `setValue()`, l'aggiornamento della proprietà è asincrono. Ciò significa che non avviene immediatamente perché il tuo widget personalizzato comunica con l'applicazione principale da un iframe. La richiesta `setValue()` viene inviata, ma il tuo codice continua a essere eseguito senza attendere che la modifica abbia effetto. Se si tenta di utilizzare getValue() subito dopo, probabilmente si otterrà il valore precedente.

Per ottenere i dati più aggiornati, usa sempre `getValue()` con una funzione di callback. Il callback viene eseguito solo quando la proprietà è stata aggiornata con successo, il che garantisce di lavorare con le informazioni più recenti.

L’esempio seguente illustra questa best practice:```// CORRETTO: Accedere al valore di una proprietà in modo asincronoconst newConfig = { setting: 'value' };setValue('config', newConfig);

// La funzione di callback verrà eseguita al completamento dell’aggiornamento della proprietà.getValue('config', function(updatedConfig) { console.log('La proprietà è stata aggiornata. La nuova configurazione è:', updatedConfig); // Esegui un’operazione con il nuovo valore garantito applyConfiguration(updatedConfig);});```

L’esempio seguente mostra cosa dovresti evitare:```// SBAGLIATO: Questo potrebbe non funzionare come previstoconst newConfig = { setting: 'value' };setValue('config', newConfig);

// Probabilmente verrà registrato il valore precedente, non quello nuovo.const currentConfig = getValue('config'); ```

6. Utilizzare intestazioni supportate per le chiamate API

Quando si effettuano chiamate API esterne da widget personalizzati, prestare attenzione alle intestazioni HTTP per evitare problemi CORS (Cross-Origin Resource Sharing). I widget personalizzati vengono eseguiti in un ambiente iframe con specifiche restrizioni di sicurezza.

Utilizza solo le intestazioni esplicitamente supportate. L'impostazione di intestazioni non supportate, come Content-Type o altre intestazioni personalizzate, può causare errori CORS e impedire il corretto funzionamento delle chiamate API.

L’esempio seguente illustra questa best practice:// CORRETTO: Semplice chiamata API senza intestazioni personalizzatefetch("https://akhatri.bulb.cloud/api/v3/w/1/tables", { headers: { Authorization: "<API key>", },}) .then((response) => response.json()) .then((data) => console.log(data)) .catch((error) => console.error("Errore:", error));

L’esempio seguente mostra cosa dovresti evitare:// SBAGLIATO: Impostare l’intestazione Content-Type causerà problemi CORSfetch("https://api.example.com/data", { headers: { Authorization: "<chiave API>", "Content-Type": "application/json", // Questo causerà errori CORS },})

Troubleshoot CORS error

If you encounter CORS errors, first remove any custom headers you've set (especially Content-Type) and test with minimal headers. The iframe security model restricts which headers can be set from custom widgets.

7. Inizializza i widget in base al tuo runtime

Per ulteriori informazioni, consultare la sezione " Versione del runtime ".

Nuovo runtime

Nel nuovo runtime, l’attributo src restituisce un file HTML "shell" vuoto. Una volta caricato, questo file "shell" invia un messaggio all’app padre. L’app padre invia il codice del widget personalizzato, le funzioni di utilità (come getValue()) e i valori iniziali delle proprietà da inserire nell’iframe.

Per questo motivo, gli eventi `DOMContentLoaded` e `window.onload` vengono attivati quando il file shell vuoto viene caricato, prima che il codice del widget venga iniettato. Qualsiasi gestore associato a questi eventi viene registrato troppo tardi: l’evento è già stato attivato, quindi la callback non viene mai eseguita. Il codice all’interno di tali gestori viene saltato silenziosamente, come mostrato nell’esempio seguente:

document.addEventListener('DOMContentLoaded', function() { // Si attiva quando SHELL viene caricato, prima dell’iniezione del codice utente // Il codice utente non è stato ancora iniettato! const myProp = getValue('myProp'); // ERRORE: getValue non è definito});

Non è necessario utilizzare DOMContentLoaded o window.onload in custom widgets. Tulip carica le risorse nel seguente ordine: librerie di terze parti, HTML dell’utente e infine JavaScript. Quando il JavaScript viene eseguito, il DOM è già disponibile. È possibile accedere direttamente agli elementi DOM senza attendere un evento di caricamento, come mostrato nell’esempio seguente:

// È sicuro chiamarlo direttamente: il DOM è già caricato quando questo codice viene eseguito.const btn = document.getElementById('my-btn');

Vecchio runtime

Nel vecchio runtime, l’attributo srcDoc inserisce il codice del widget personalizzato nell’iframe, insieme a funzioni di utilità come getValue() e ai valori iniziali delle proprietà. Poiché il codice del widget fa parte del documento sin dall’inizio, getValue() e le proprietà sono disponibili non appena viene caricato il contenuto dell’iframe.Di conseguenza, DOMContentLoaded e window.onload funzionano come previsto nel vecchio runtime, come illustrato nell’esempio seguente:

document.addEventListener('DOMContentLoaded', function() { // Si attiva quando l’HTML dell’utente è pronto // Le proprietà sono già disponibili const myProp = getValue('myProp'); // Funziona immediatamente});

{{snippet.Hai trovato quello che cercavi?}}