Template con ExtJS

Uso dei template con ExtJS

Molti componenti ExtJS usano i template, in alcuni casi si potrebbe dire che lo sviluppo stesso dei componenti è basato sui template. In questo articolo vorrei soffermarmi soprattutto nell'uso di template con liste e griglie.

Le liste, per esempio, hanno una proprietà itemTpl che indica un template da applicare a tutte le righe. Il template in questo caso è fondamentalmente una stringa con un frammento di HTML che verrà ripetuto per ogni elemento/riga della lista. All'interno del template è possibile far riferimento ai campi dello store tramite i loro nomi tra parentesi graffa. Per esempio:

{
    xtype: 'list',
    store: {
        data: [...]
    },
    itemTpl: '<b>{name}</b>'
}

È anche possibile applicare delle funzioni ai singoli campi:

{
    xtype: 'list',
    store: {
        data: [...]
    },
    itemTpl: '<b>{name:htmlEncode}</b> {hireDate:date("d/m/Y")}'
}

Ottenendo un risultato come il seguente:

Esempio

In questo esempio, supponendo che il campo hireDate sia una data, applichiamo la funzione Ext.util.Format.date per visualizzare la data in un determinato formato. Per il nome invece uso la funzione Ext.util.Format.htmlEncode, così, se nel nome ci fosse qualche carattere speciale HTML (<, >, ecc.), questi vengono visualizzati correttamente senza essere interpretati dal browser. Indicando le funzioni in questo modo, separate dal nome del campo tramite due punti (:), possiamo usare tutte le funzioni della classe Ext.util.Format.

Tramite i template possiamo anche:

  • Usare costrutti di controllo (if/else)
  • Semplici espressioni matematiche
  • Eseguire dei loop per visualizzare dati complessi (array/oggetti)
  • Eseguire codice arbitrario
  • E molto altro

Per altri dettagli sono disponibili numerosi esempi sulla guida ufficiale alla voce Template e XTemplate.

La cosa più ostica riguardo ai template è che richiedono una discreta conoscenza di HTML e CSS; infatti il template non è altro che un frammento di HTML con dei marcatori che permettono a ExtJS di costruire il risultato finale. In effetti in questo caso parte della semplicità di ExtJS, che normalmente ci nasconde la complessità del browser, viene meno.

Flex layout

In questo esempio quello che vorrei ottenere è qualcosa di simile a questo:

Sencha fiddle

Analizzando la struttura delle singole righe ci accorgiamo che in pratica ci sono tre colonne:

  • La prima contiene l'immagine del profilo
  • La seconda due elementi, il nome e l'immagine, che vengono espansi fino ad occupare tutto lo spazio disponibile
  • La terza contiene la data

Colonne

Questo tipo di disposizione, abbastanza comune, si adatta perfettamente al layout CSS denominato FlexBox. Un'ottima guida è quella che si trova su CSS-Tricks. Il flexbox è un layout che, applicato ad un elemento, permette di disporre i propri figli in colonne (default) o righe.

Il template che useremo quindi potrebbe essere una cosa del genere:

<div class="list-item-container">
    <img class="list-item-photo" src="image-{id}.jpg">
    <div class="list-item-growing">',
        <div class="list-item-name">{name}</div>
        <div><a href="mailto:{email}">{email}</a></div>
    </div>
    <div class="list-item-date"><span class="x-fa fa-clock"></span> {date}</div>
</div>

In effetti c'è un elemento div principale (class="list-item-container") che ha tre figli, il tag img, un div (class="list-item-growing") con nome ed e-mail ed un ultimo div (class="list-item-date") con un'icona e la data. Gli elementi tra parentesi graffe saranno letti dallo store.

Innanzi tutto dobbiamo dire al browser che il div principale usa il layout flexbox e che gli elementi al suo interno devono essere allineati verticalmente.

.list-item-container {
    /* Use "FLEX" layout */
    display: flex;
    /* All columns are vertically centered */
    align-items: center;
}

Per la prima colonna forziamo la dimensione dell'immagine a 50px e usiamo border-radius al 50% per renderla circolare.

.list-item-photo {
    /* Force the image width to 50px */
    width: 50px;
    /* Force the image height to 50px */
    height: 50px;
    /* With a border radius of 50% with get a perfect circle */
    border-radius: 50%;
    /* A little margin on the right */
    margin-right: 15px;
}

La seconda colonna invece dovrà occupare tutto lo spazio in modo che la terza sia allineata a destra. Per questo è sufficiente usare la proprietà flex-grow:

.list-item-growing {
    /* This column fit all the remaining space */
    flex-grow: 1;
}

In questo modo però il risultato sarà come l'immagine seguente:

Immagine errata

Apparentemente sembra che la proprietà flex-grow non abbia funzionato. In realtà il problema è che ExtJS dimensiona la larghezza delle righe in base a quello che contengono. Dobbiamo invece forzare la larghezza delle righe uguale alla larghezza dell'intera lista. Per farlo dobbiamo usare la configurazione itemContentCls e "agganciarla" ad una classe CSS che imposti la width uguale al 100%.

.item-container {
    /* Force the items to fit all the available width */
    width: 100%;
}

Il sorgente completo della lista è il seguente:

{
    xtype: 'list',
    store: {
        data: [...]
    },
    itemContentCls: 'item-container',
    itemTpl: [
        '<div class="list-item-container">',
        '<img class="list-item-photo" src="https://randomuser.me/api/portraits/thumb/lego/{id}.jpg">',
        '<div class="list-item-growing">',
            '<div class="list-item-name">{name:htmlEncode}</div>',
            '<div><a href="mailto:{email}">{email}</a></div>',
        '</div>',
        '<div class="list-item-date"><span class="x-fa fa-clock"></span> {date}</div>',
        '</div>'
    ]
}

Conclusioni

Questo è solo un semplice esempio ma le possibilità dei template sono veramente enormi. Il codice che abbiamo visto può essere usato così com'è per le colonne di una griglia o gli elementi di una combobox. Ma in realtà molti componenti di ExtJS possono essere personalizzati tramite template e possono essere anche creati nuovi componenti proprio a partire da un template.

Potete vedere il codice completo dell'esempio qui:

https://fiddle.sencha.com/#view/editor&fiddle/3mim