Datatables che passione!

Tutta la potenza del lato server

--

Tempo addietro, per uno dei nostri progetti realizzati con Plone, avevamo sviluppato una soluzione basata sul noto plugin per jQuery, DataTables, per la gestione tabellare di documenti. L’approccio iniziale consisteva nel classico utilizzo del plugin con la gestione dei dati lato client: dopo aver prodotto il codice html della tabella con tutti i dati necessari, si provvedeva ad applicare a quest’ultima il plugin per la formattazione dei dati.

A seguito di un cambio di requisiti del progetto in questione, è stato necessario ripensare il modo in cui gestire la visualizzazione dei dati. L’approccio utilizzato, infatti, presenta i propri limiti se il numero di righe da visualizzare diventa consistente, introducendo un notevole ritardo nel caricamento della pagina.

Per rendere accettabili i tempi di risposta si è quindi provveduto a re-implementare l’utilizzo di DataTables utilizzando l’approccio server-side, basato su richieste ajax al server che restituisce i dati richiesti.

Per procedere in questo senso si è innanzitutto semplificato il codice html della tabella, che in questo caso non contiene altro che lo schema di base:

<table id='documentiqualita' class="">
<thead>
<tr>
<th i18n:translate="doc_structures">Structure</th>
<th i18n:translate="doc_kind">Kind</th>
<th i18n:translate="doc_description">Description</th>
<th i18n:translate="doc_title">Title</th>
<th i18n:translate="doc_revision">Revision</th>
<th i18n:translate="doc_data">Data</th>
<th i18n:translate="doc_file">File</th>
<th i18n:translate="" style="display:none">Useful</th>
<th i18n:translate="" style="display:none">Interest Area</th>
</tr>
</thead>
</table>

Successivamente si è configurato il plugin per operare in modalità server-side. Nel codice riportato sotto si nota la chiamata ajax alla vista di Plone che provvede a recuperare i dati da visualizzare:

var table = $('#documentiqualita').dataTable({
"aaSorting": [[ 2, "asc" ]],
"bServerSide": true,
"bDeferRender": true,
'bProcessing': true,
"sAjaxSource": portal_url + '/ajax_topic_view',
"fnInitComplete": function(oSettings, json) {
table.hide()
},

Anche la gestione della formattazione dei dati da visualizzare, in caso di esigenze particolari, viene gestito dal plugin. Per le nostre esigenze viene definito il modo in cui si desidera visualizzare i dati recuperati, a seconda della colonna della tabella interessata:

"aoColumnDefs": [{
"mRender": function ( data, type, row ) {
result = '<ul>'
$.each(data, function(i) {
result = result+'<li>'+data[i]+'</li>'});
result = result + '</ul>'
return result
},
"aTargets": [0]
},
...
...
...
]

Elaborazione dati server-side

Nel momento in cui si configura DataTables in modalità server-side, occorre ridefinire tutte le funzionalità che prima venivano svolte dal client. Nel nostro caso specifico è stato necessario definire lato server le procedure per effettuare paginazione, filtering, ricerca di testo e ordinamento della tabella per colonne. Ad ogni azione eseguita dall’utente per la ricerca in tabella corrisponde quindi la generazione di una nuova richiesta ajax che invia determinati parametri al server:

Esempio di parametri inviati per una ricerca. In questo caso si vogliono trovare i documenti corrispondenti ai filtri AP, MO e contenenti la parola ‘isto’ nella descrizione o nel titolo

Il server prepara la query per interrogare il catalogo di Plone utilizzando i dati ricevuti. Ad esempio, per gestire ll filtering dei dati sugli indici di interesse:

def get_filtered(self):
query = {}
search_indexes = {
'sSearch_0': 'getStructures',
'sSearch_1': 'getCategories',
'sSearch_8': 'getInterest_area',
'sSearch_7': 'getUseful_for',
}

value = self.request.get('sSearch')
...
...
...
for index in [0, 1, 7, 8]:
key = 'sSearch_%s' % (index)
value = self.request.get(key)
if value:
query[search_indexes[key]] = value

return query

I dati recuperati dal catalogo di Plone vengono infine restituiti in formato json al client:

def search_docs(self):
self.request.response.setHeader('Content-Type', 'application/json')
result_list = []
brains = self.make_query()
for brain in brains:
...
...
...
data_res = [
struct,
kinds,
obj.Description(),
obj.Title(),
obj.getRevision(),
doc_date,
file_mime_type,
obj.absolute_url(),
obj.getFile().getFilename(),
self.have_attachments(obj),
obj.getUseful_for(),
obj.getInterest_area(),
]
result_list.append(data_res)response = {
'sEcho': self.echo_value,
'iTotalRecords': total_elements,
'iTotalDisplayRecords': self.total_filtered_records,
'aaData': result_list
}
return json.dumps(response)
Presentazione della tabella all’utente finale

Concludendo…

Questo plugin si è dimostrato davvero versatile e ottimamente documentato. Grazie alle svariate possibilità di configurazione siamo risusciti a gestire il cambio di implementazione in maniera semplice. I risultati si sono tradotti in una drastica riduzione della latenza iniziale che ha fornito al cliente un netto miglioramento nell’usabilità del prodotto.

--

--