In questo paragrafo affrontiamo un ulteriore utilizzo delle datamap per ottenere liste innestate o gestire strutture ricorsive come alberi, organigrammi ed altri elementi simili.
Come primo esempio vogliamo realizzare una lista di categorie che mostri, per ognuna di esse, anche l’elenco dei prodotti. Nell’immagine seguente viene mostrato un esempio dell’obiettivo da raggiungere.
Per ottenere questa struttura, è possibile partire dalla lista delle categorie come indicato nel primo paragrafo del capitolo, poi è necessario innestare una seconda lista all’interno della prima. Questo avviene implementando tre strutture all’interno della videata.
Innanzitutto occorre aggiungere all’interno della datamap dmCategorie una datamap interna, che chiameremo dmProdotti. Siccome essa è contenuta in un’altra datamap, è chiamata datamap innestata.
In secondo luogo, per generare la lista dei prodotti di una categoria, dmProdotti ha bisogno di un suo template che deve essere all’interno del template della datamap dmCategorie.
Infine, l’evento onRowComposition della datamap esterna deve effettuare il caricamento dei dati della datamap interna. In particolare viene usato il seguente codice:
$dmCategorie.onRowComposition = function (row, template)
{
let c = yield App.NBE.Product.loadCollection(app, {CategoryID :
row.CategoryID});
$dmProdotti.collection = c;
};
All’apertura della videata, la datamap dmCategorie, avendo il flag AutoLoad attivo, esegue la query e carica la lista delle categorie. Per ogni categoria, crea un clone del template visuale e associa i dati della categoria ad esso.
In questo caso però, è presente anche dmProdotti, una datamap innestata all’interno di dmCategorie. Quindi, subito dopo aver creato il clone del template per una riga di dmCategorie, viene clonata anche la datamap dmProdotti, questo clone viene associato alla riga generata per la categoria ed infine, come template di riga della datamap clone, viene utilizzato l’elemento corrispondente al template di dmProdotti all’interno del clone del template di riga della datamap esterna.
A questo punto viene notificato l’evento onRowComposition per la datamap esterna, che si occupa di caricare la collection dei prodotti della categoria per cui l’evento è stato notificato e di usarla come sorgente dati per il clone della datamap interna. Questa operazione viene effettuata dall’istruzione: $dmProdotti.collection = c dove l’espressione $dmProdotti usata all’interno del codice dell’evento onRowComposition indica proprio il clone della datamap generato per la riga in fase di composizione.
A sua volta, la datamap dmProdotti clonata genera i propri elementi visuali in base ai dati ricevuti, clonando il suo template di riga, impostando le proprietà visuali e così via.
Per quanto riguarda l’accesso alle proprietà delle datamap da parte degli elementi visuali, occorre tenere presente della struttura visuale degli elementi stessi. Se ad esempio al clic sul nome di un prodotto volessimo visualizzare il messaggio “questo è il prodotto <nome> della categoria <nome>”, potremmo referenziare il nome del prodotto con l’espressione this.row.ProductName, come nei casi precedenti; mentre, per arrivare alla corrispondente DataRow della datamap esterna, possiamo risalire nella catena degli elementi visuali passando dal template della datamap interna a quello della datamap esterna. In sintesi l’espressione da utilizzare è la seguente:
let nomeCategoria = this.row.element.parent.row.CategoryName;
Infatti this.row.element è il clone del template generato per il prodotto cliccato. Il parent di tale elemento appartiene invece al clone del template generato per la categoria a cui il prodotto cliccato appartiene. A tale elemento è associata la proprietà row che in questo caso contiene i dati della categoria. In questo modo abbiamo raggiunto il nostro obiettivo.
Datamap ricorsive #
L’uso delle datamap innestate permette di costruire liste multi-livello nelle quali, tuttavia, il numero di livelli deve essere conosciuto a design time. Se si devono gestire strutture ricorsive, come ad esempio le strutture ad albero, una datamap innestata non è sufficiente.
Immaginiamo di voler sfruttare il legame ricorsivo dei record della tabella impiegati per visualizzare un organigramma come il seguente:
Per ottenere il risultato possiamo creare la struttura mostrata nell’immagine seguente, in cui:
- È presente una datamap di primo livello che si occupa di caricare i dati di primo livello. In questo caso i presidenti, quelli che non dipendono da nessuno.
- All’interno della datamap di primo livello è presente una datamap innestata che viene caricata nell’evento onRowComposition della precedente. Essa contiene gli impiegati che dipendono direttamente dai presidenti.
- Questa seconda datamap ha il flag Recursive attivo, quindi quando essa genera una riga, all’interno di questa riga viene nuovamente clonata anche la datamap stessa, che in questo modo diventa di terzo livello.
- La datamap di secondo livello carica gli impiegati che rispondono all’impiegato attuale e poi associa questa collection al clone di sé stessa che trova come proprietà della riga.
È interessante vedere il codice dell’evento onRowComposition della datamap di secondo livello:
$dmImpiegati.onRowComposition = function (row, template)
{
row.dmImpiegati.collection = yield App.NBE.Impiegato.loadCollection(app,
{ReportsTo : row.EmployeeID});
};
Il riferimento alla datamap di secondo livello clonata per diventare di terzo livello viene ottenuto dall’espressione row.dmImpiegati. La datamap di secondo livello diventa quindi il sistema per innescare la ricorsione all’n-esimo livello in quanto il suo codice, partendo dai dati del livello attuale, si occupa di caricare quelli del livello successivo.
A livello visuale, l’innesco della ricorsione avviene in maniera analoga. Quando la datamap di secondo livello viene clonata per generare il terzo, viene clonato anche il listbox, l’elemento parent del template di riga, destinato a contenere tutti gli elementi delle righe della datamap. Il clone del listbox viene aggiunto agli elementi della riga della datamap di secondo livello e verrà usato dalla datamap di terzo livello per contenere le sue righe.
In questo modo è possibile costruire strutture ricorsive a n livelli sia come struttura di datamap innestate in memoria che come struttura di elementi visuali generati da queste datamap.