Creare reti di dati Ajax con CodeIgniter e jQuery

In questa lezione creeremo una libreria CodeIgniter che ci consente di generare automaticamente griglie di dati per la gestione di qualsiasi tabella di database. Spiegherò ogni passo necessario per creare questa classe; quindi probabilmente imparerai alcune nuove tecniche / concetti OOP nel processo!

Come bonus, procederemo alla scrittura di un codice jQuery che consentirà a un utente di aggiornare il contenuto della griglia di dati senza dover attendere un aggiornamento della pagina.


Notare che?

Questo tutorial presume che tu abbia una comprensione modesta del CodeIgniter e dei framework jQuery.

Cos'è una griglia di dati?

Un datagrid è una tabella che visualizza i contenuti di un database o di una tabella insieme ai controlli di ordinamento.

Un datagrid è una tabella che visualizza i contenuti di un database o di una tabella insieme ai controlli di ordinamento. In questo tutorial, avremo il compito di fornire questa funzionalità, ma anche di salvare l'utente dall'attesa che la pagina si aggiorni ogni volta che viene eseguita un'operazione. Grazie a jQuery, questo sarà un compito abbastanza semplice!

Che dire degli utenti che non hanno Javascript abilitato? Non preoccuparti, li compenseremo anche noi!


Passaggio 1: creare una classe di generatore di griglia di dati

Vogliamo creare uno strumento che ci consenta di creare dati dinamici in modo dinamico per qualsiasi tabella di database che abbiamo. Ciò significa che il codice non è legato a una specifica struttura di tabella e, quindi, è indipendente dai dati stessi. Tutto il codificatore (lo sviluppatore che usa la nostra classe) deve sapere è il nome della tabella da trasformare in una griglia e la chiave primaria per quella tabella. Ecco la prefazione della classe che svilupperemo per la maggior parte di questo tutorial:

La classe Datagrid potrebbe essere aggiunta alla cartella application / library, ma la aggiungeremo come helper al framework CodeIgniter. Perché? Poiché il caricamento delle librerie non ci consente di passare argomenti al costruttore della classe, quindi il caricamento come helper risolverà il problema. Questo punto avrà più senso per te quando avremo finito di scrivere il costruttore.

Il metodo di costruzione della classe

funzione pubblica __construct ($ tbl_name, $ pk_col = 'id') $ this-> CI = & get_instance (); $ This-> CI-> load-> database (); $ this-> tbl_fields = $ this-> CI-> db-> list_fields ($ tbl_name); if (! in_array ($ pk_col, $ this-> tbl_fields)) throw new Exception ("colonna chiave primaria '$ pk_col' non trovata nella tabella '$ tbl_name'");  $ this-> tbl_name = $ tbl_name; $ this-> pk_col = $ pk_col; $ This-> CI-> load-> library ( 'table'); 

Abbiamo già molto da fare; ma non preoccuparti, come spiegherò tutto per te nel prossimo paragrafo.

Il costruttore prende due argomenti: il primo è il nome della tabella nel database che si desidera visualizzare come un datagrid all'utente; il secondo param è il nome della colonna che serve come chiave primaria per quella tabella (ne parleremo più avanti). All'interno del corpo del costruttore, istanziamo l'oggetto CodeIgniter, l'oggetto del database e la classe / libreria della tabella HTML. Tutti questi saranno necessari durante la vita di un oggetto Datagrid e sono già integrati nel framework CI. Si noti che controlliamo anche se la chiave primaria esiste realmente nella tabella data e, in caso contrario, lanciamo un'eccezione che riporta l'errore. Ora il $ This-> tbl_fields la variabile membro sarà disponibile per un uso successivo, quindi non dobbiamo recuperare di nuovo il database.

"Possiamo usare il comando, $ CI-> db-> list_fields ($ nome_tabella) per recuperare i nomi di tutti i campi che ha una tabella. Tuttavia, per prestazioni migliori, consiglio di memorizzare i risultati nella cache. "

Metodo per personalizzare le intestazioni delle tabelle

funzione pubblica setHeadings (array $ intestazioni) $ this-> intestazioni = array_merge ($ this-> intestazioni, $ intestazioni); 

Ciò consente di personalizzare i titoli della tabella della griglia di dati, ovvero, con esso, è possibile sovrascrivere i nomi delle colonne originali per determinati campi della tabella. Ci vuole un associativo schieramento, In questo modo: regdate => "Registration Date". Invece del solo "Regdate" tecnico come colonna per quel tipo di dati, abbiamo al suo posto un titolo più leggibile. Il codice responsabile dell'applicazione delle intestazioni verrà rivelato a breve.

Metodo per ignorare / nascondere i campi della tabella

public function ignoreFields (array $ fields) foreach ($ fields as $ f) if ($ f! = $ this-> pk_col) $ this-> hide_cols [] = $ f; 

ignoreFields riceve un schieramento contenente i campi da ignorare quando si prelevano i dati dal database. Questo è utile quando abbiamo tabelle con molti campi, ma vogliamo solo nasconderne un paio. Questo metodo è abbastanza intelligente per tenere traccia del tentativo di ignorare il campo della chiave primaria e quindi saltarlo. Questo è così perché la chiave primaria non può essere ignorato per motivi tecnici (vedrete perché a breve). Tuttavia, se si desidera nascondere la colonna della chiave primaria dall'appare nell'interfaccia utente, è possibile utilizzare il comando hidePkCol metodo:

funzione pubblica hidePkCol ($ bool) $ this-> hide_pk_col = (bool) $ bool; 

Questo metodo riceve un valore booleano per indicare se vogliamo nascondere la colonna della chiave primaria in modo che non venga visualizzata nella griglia di dati. A volte, è una brutta idea mostrare il pkey dati, che di solito è un codice numerico senza alcun significato per l'utente.

Metodo dell'istanza successiva:

funzione privata _selectFields () foreach ($ this-> tbl_fields come $ campo) if (! in_array ($ field, $ this-> hide_cols)) $ this-> CI-> db-> select ($ field); // nascondi l'intestazione della colonna pk? se ($ campo == $ this-> pk_col && $ this-> hide_pk_col) continua; $ intestazioni [] = isset ($ this-> titoli [$ campo])? $ this-> intestazioni [$ campo]: ucfirst ($ campo);  if (! empty ($ headings)) // anteporre una casella di spunta per attivare array_unshift ($ headings, ""); $ this-> CI-> table-> set_heading ($ intestazioni);

Qui abbiamo un metodo di supporto; è per questo che ha il modificatore "privato" e ha come prefisso un carattere di sottolineatura (convenzione di codice). Sarà usato dal creare() metodo - spiegato brevemente - per avere i campi della tabella appropriati selezionati e anche i titoli appropriati impostati sulla tabella (generatore) oggetto. Notare la seguente riga:

$ intestazioni [] = isset ($ this-> titoli [$ campo])? $ this-> intestazioni [$ campo]: ucfirst ($ campo);

Qui è dove applichiamo le intestazioni personalizzate o ricorriamo a quelle predefinite se non ne viene data nessuna. Se la pk la colonna dovrebbe essere nascosta dal display, quindi la sua intestazione verrà saltata. Notare anche la seguente riga:

array_unshift ($ intestazioni,"");

Il comando sopra indica al programma di anteporre una casella di controllo "Master" come prima intestazione della tabella. Questa casella di controllo è diversa dalle altre caselle di controllo nella griglia in quanto consente a un utente di selezionare o deselezionare tutte le caselle di controllo in un solo passaggio. Questa funzionalità di commutazione verrà implementata in pochi istanti con un semplice frammento di codice jQuery.

Metodo per generare / rendere il Datagrid

Ora arriva la cosa che fa il vero lavoro per noi:

funzione pubblica generate () $ this -> _ selectFields (); $ rows = $ this-> CI-> db -> from ($ this-> tbl_name) -> get () -> result_array (); foreach ($ rows as & $ row) $ id = $ row [$ this-> pk_col]; // anteporre una casella di controllo per abilitare la selezione di elementi / righe array_unshift ($ row, ""); // nascondi la cella della colonna pk? if ($ this-> hide_pk_col) unset ($ row [$ this-> pk_col]) restituisce $ this-> CI-> table-> generate ($ rows) ;

Il creare il metodo, come suggerisce il nome, è responsabile della generazione della griglia di dati stessa. Dovresti chiamare questo metodo solo dopo aver configurato l'oggetto in base alle tue esigenze. La prima cosa che fa è chiamare il $ This -> _ selectFields () metodo per eseguire le azioni che abbiamo spiegato in precedenza. Ora deve recuperare tutte le righe dal database e quindi passarle in loop, aggiungendo caselle di controllo all'inizio di ogni riga:

// anteporre una casella di controllo per abilitare la selezione di elementi / righe array_unshift ($ row, "");

Dentro il per ciascuno loop sul creare metodo, se il $ This-> hide_pk_col flag è impostato su vero, quindi dobbiamo disinserire la voce della chiave primaria nel file $ row array quindi non verrà visualizzato come una colonna quando il $ This-> CI-> Tavolo oggetto elabora tutte le righe e genera l'output html finale. A questo punto, è possibile rimuovere la chiave primaria, se necessario, perché non abbiamo più bisogno di quelle informazioni. UN

Ma cosa fa l'utente con le righe selezionate / controllate? Per rispondere a questo, ho preparato alcuni altri metodi. Il primo ci consente di creare "pulsanti di azione" senza dover conoscere alcun dettaglio tecnico su come il sistema di rete funziona internamente:

Metodo per l'aggiunta di pulsanti a un modulo griglia dati

funzione statica pubblica createButton ($ nome_azione, $ etichetta) ritorno "";

Basta passare il nome dell'azione come primo argomento e un secondo argomento per indicare l'etichetta per il pulsante generato. UN classe l'attributo viene generato automaticamente per quel pulsante in modo che possiamo giocarci più facilmente quando stiamo lavorando con esso nel nostro JavaScript. Ma come facciamo a sapere se un determinato pulsante di azione è stato premuto dall'utente? La risposta può essere trovata nel prossimo metodo:

public static function getPostAction () // ottiene il nome dell'azione inviata (se presente) if (isset ($ _ POST ['dg_action'])) restituisce la chiave ($ _ POST ['dg_action']); 

Sì! Un altro metodo statico che ci aiuta quando abbiamo a che fare con le forme. Se è stata inviata una griglia di dati, questo metodo restituirà il nome dell'azione (o "operazione") associata a quell'evento di invio. Inoltre, un altro utile strumento per elaborare i nostri moduli Datagrid è?

public static function getPostItems () if (! empty ($ _ POST ['dg_item'])) return $ _POST ['dg_item'];  return array (); 

? che restituisce un schieramento contenente il selezionato ids in modo da poter tenere traccia delle righe selezionate sulla griglia e quindi eseguire un'azione con esse. Come esempio di cosa si può fare con una selezione di righe ids, ho preparato un altro metodo - questo è un metodo di istanza, e non uno statico, perché fa uso delle risorse di istanza dell'oggetto per fare la sua attività:

public function deletePostSelection () // rimuove gli elementi selezionati dal db if (! empty ($ _ POST ['dg_item'])) restituisce $ this-> CI-> db -> da ($ this-> tbl_name) -> dove_in ($ this-> pk_col, $ _ POST ['dg_item']) -> delete (); 

Se è stata selezionata almeno una casella di controllo, il deletePostSelection () il metodo genererà ed eseguirà un'istruzione SQL come la seguente (supponiamo $ Nome_tabella = 'my_table' e $ Pk_col = 'id'):

CANCELLA DA my_table DOVE ID IN (1,5,7,3, ecc.?)

? che rimuoverà efficacemente le file selezionate dal livello persistente. Potrebbero esserci più operazioni che potresti aggiungere a una griglia di dati, ma ciò dipenderà dalle specifiche del tuo progetto. Come consiglio, potresti estendere questo corso, per esempio, InboxDatagrid, così, oltre il deletePostSelection metodo, potrebbe includere operazioni extra, come ad esempio moveSelectedMessagesTo ($ posto), eccetera?

Mettendo tutto insieme

Ora, se hai seguito questo tutorial passo per passo, dovresti avere qualcosa di simile al seguente:

class Datagrid private $ hide_pk_col = true; private $ hide_cols = array (); private $ tbl_name = "; private $ pk_col ="; private $ headings = array (); private $ tbl_fields = array (); function __construct ($ tbl_name, $ pk_col = 'id') $ this-> CI = & get_instance (); $ This-> CI-> load-> database (); $ this-> tbl_fields = $ this-> CI-> db-> list_fields ($ tbl_name); if (! in_array ($ pk_col, $ this-> tbl_fields)) throw new Exception ("colonna chiave primaria '$ pk_col' non trovata nella tabella '$ tbl_name'");  $ this-> tbl_name = $ tbl_name; $ this-> pk_col = $ pk_col; $ This-> CI-> load-> library ( 'table');  public function setHeadings (array $ headings) $ this-> headings = array_merge ($ this-> intestazioni, $ intestazioni);  public function hidePkCol ($ bool) $ this-> hide_pk_col = (bool) $ bool;  public function ignoreFields (array $ fields) foreach ($ fields as $ f) if ($ f! = $ this-> pk_col) $ this-> hide_cols [] = $ f;  funzione privata _selectFields () foreach ($ this-> tbl_fields come $ campo) if (! in_array ($ field, $ this-> hide_cols)) $ this-> CI-> db-> select ($ campo ); // nascondi l'intestazione della colonna pk? se ($ campo == $ this-> pk_col && $ this-> hide_pk_col) continua; $ intestazioni [] = isset ($ this-> titoli [$ campo])? $ this-> intestazioni [$ campo]: ucfirst ($ campo);  if (! empty ($ headings)) // anteporre una casella di spunta per attivare array_unshift ($ headings, ""); $ this-> CI-> table-> set_heading ($ headings; public function generate () $ this -> _ selectFields (); $ rows = $ this-> CI-> db -> from ( $ this-> tbl_name) -> get () -> result_array (); foreach ($ rows as & $ row) $ id = $ row [$ this-> pk_col]; // antepone una casella per abilitare la selezione degli elementi array_unshift ($ row, ""); // nascondi la colonna pk? if ($ this-> hide_pk_col) unset ($ row [$ this-> pk_col]); restituisce $ this-> CI-> table-> generate ($ rows);  funzione statica pubblica createButton ($ nome_azione, $ etichetta) ritorno ""; public static function getPostAction () // ottiene il nome dell'azione inviata (se presente) if (isset ($ _ POST ['dg_action'])) return key ($ _ POST ['dg_action']); public funzione statica getPostItems () if (! empty ($ _ POST ['dg_item'])) return $ _POST ['dg_item']; return array (); public function deletePostSelection () // rimuove gli elementi selezionati dal db if (! empty ($ _ POST ['dg_item'])) restituisce $ this-> CI-> db -> da ($ this-> tbl_name) -> dove_in ($ this-> pk_col, $ _ POST ['dg_item' ]) -> delete ();

Avviso: non dimenticare di salvare questo file come datagrid_helper.php, e posizionarlo in "application / helper /"


Passaggio 2: test della classe di supporto Datagrid con un controller CodeIgniter

Creeremo ora un semplice controller di test e cariceremo la classe Datagrid come helper nel suo costruttore. Ma prima dovremmo definire una tabella di database fittizia e popolarla con alcuni dati di esempio.

Esegui il seguente SQL per creare il database e la tabella utente:

CREATE DATABASE 'dg_test'; CREATE TABLE 'users' ('id' int (11) NOT NULL AUTO_INCREMENT, 'username' varchar (80) NOT NULL, 'password' varchar (32) NOT NULL, 'email' varchar (255) NOT NULL, UNQUE KEY ' id '(' id ')) ENGINE = MyISAM DEFAULT CHARSET = latin1 AUTO_INCREMENT = 5;

Successivamente, aggiungiamo alcuni utenti ad esso:

INSERISCI 'utenti' ('id', 'username', 'password', 'email') VALUES (1, 'david', '12345', '[email protected]'), (2, 'maria', '464y3y', '[email protected]'), (3, 'alejandro', 'a42352fawet', '[email protected]'), (4, 'emma', 'f22a3455b2','[email protected] ');

Ora, salva il seguente codice come "test.php,"e aggiungilo alla cartella" application / controller ":

load-> helper (array ( 'DataGrid', 'url')); $ this-> Datagrid = new Datagrid ('users', 'id');  function index () $ this-> load-> helper ('form'); $ This-> load-> library ( 'sessione'); $ This-> Datagrid-> hidePkCol (true); $ This->> Datagrid- setHeadings (array ( 'e-mail' => 'e-mail')); $ This-> Datagrid-> ignoreFields (array ( 'password')); if ($ error = $ this-> session-> flashdata ('form_error')) echo "$ error"; echo form_open ('test / proc'); echo $ this-> Datagrid-> generate (); echo Datagrid :: createButton ('delete', 'Delete'); echo form_close (); proc proc ($ request_type = ") $ this-> load-> helper ('url'); if ($ action = Datagrid :: getPostAction ()) $ error = ""; switch ($ action) case 'delete': if (! $ this-> Datagrid-> deletePostSelection ()) $ error = 'Gli articoli non possono essere cancellati';  rompere;  if ($ request_type! = 'ajax') $ this-> load-> library ('session'); $ This-> session-> set_flashdata ( 'form_error', $ errore); reindirizzare ( 'test / index');  else echo json_encode (array ('error' => $ error));  else die ("Richiesta non valida"); ?>

Un'istanza di questa classe viene creata e passata come riferimento a $ This-> Datagrid membro. Si noti che recupereremo i dati da una tabella denominata "utenti" la cui chiave primaria è la colonna "id"; quindi, sul metodo index, eseguiamo i seguenti passaggi: configurare l'oggetto Datagrid, renderlo all'interno di un modulo con un pulsante di eliminazione aggiunto e vedere se tutto funziona come previsto:

Domanda: cosa succede quando il modulo viene inviato?

Risposta: Il "Test :: proc ()"il metodo si occupa dell'elaborazione del modulo e della scelta della corretta operazione da applicare contro il ids che sono stati selezionati dal mittente del modulo. Si occupa anche delle richieste AJAX, quindi restituirà un oggetto JSON al client. Questa funzione che riconosce AJAX sarà utile quando jQuery entrerà in azione, che è proprio ora!

"È sempre una buona idea creare applicazioni web che compensino quando JavaScript / AJAX non è disponibile, in questo modo, alcuni utenti avranno un'esperienza più ricca e più veloce, mentre quelli senza JavaScript abilitato saranno comunque in grado di utilizzare l'applicazione normalmente."


Passaggio 3: implementazione di Ajax (jQuery to the Rescue!)

Quando l'utente fa clic sul pulsante (o su qualsiasi altro pulsante di azione), vorremmo, forse, impedire che la pagina si ricarichi e debba generare nuovamente tutto; questo potrebbe far addormentare l'utente della nostra applicazione! Evitare questo problema non sarà un compito difficile se ci atteniamo alla libreria jQuery. Poiché non si tratta di un tutorial per principianti, non esaminerò tutti i dettagli relativi a come ottenere la libreria, come includerla nella pagina, ecc. Ci si aspetta che tu conosca questi passaggi da solo.

Crea una cartella, denominata "js", aggiungi la libreria jQuery all'interno e crea un file di visualizzazione, denominato users.php. Apri questo nuovo file e aggiungi:

  Gestione degli utenti     Datagrid-> hidePkCol (true); if ($ error = $ this-> session-> flashdata ('form_error')) echo "$ error"; echo form_open ('test / proc', array ('class' => 'dg_form')); echo $ this-> Datagrid-> generate (); echo Datagrid :: createButton ('delete', 'Delete' ); echo form_close ();?>  

Ti sei reso conto di aver allontanato il codice Test :: Indice e nel nuovo script di visualizzazione? Questo significa che dobbiamo cambiare il Test :: index () metodo di conseguenza:

function index () $ this-> load-> helper ('form'); $ This-> load-> library ( 'sessione'); $ This-> load-> view ( 'utenti'); 

Così va meglio. Se si desidera aggiungere uno stile alla griglia, è possibile utilizzare il seguente CSS (o creare un layout migliore da soli):

 .dg_form table border: 1px argento massiccio;  .dg_form th background-color: gray; font-family: "Courier New", Courier, mono; font-size: 12px;  .dg_form td background-color: gainsboro; font-size: 12px;  .dg_form input [type = submit] margin-top: 2px; 

Ora, per favore, crea un file "datagrid.js", mettilo nella directory "js" e inizia con questo codice:

$ (function () // cose interessanti qui?)

All'interno di questa chiusura, scriveremo il codice che verrà incaricato di controllare determinati eventi di invio una volta che la pagina è stata caricata completamente. La prima cosa che dobbiamo fare è tenere traccia quando un utente fa clic su un pulsante di invio sul modulo della griglia di dati, quindi invia i dati da elaborare sul server.

 $ ('. dg_form: submit'). click (function (e) e.preventDefault (); var $ form = $ (this) .parents ('form'); var nome_azione = $ (this) .attr (' class '). replace ("dg_action_", ""); var action_control = $ (''); $ Form.append (action_control); var post_data = $ form.serialize (); action_control.remove (); var script = $ form.attr ('action') + '/ ajax'; $ .post (script, post_data, function (resp) if (resp.error) alert (resp.error); else switch (action_name) case 'delete': // rimuove le righe cancellate dalla griglia $ form .find ('. dg_check_item: checked'). parents ('tr'). remove (); break; case 'anotherAction': // fa qualcos'altro? break;, 'json'))

In alternativa, avremmo potuto iniziare con qualcosa come: $ ('. dg_form'). submit (function (e) ?). Tuttavia, dal momento che voglio tenere traccia di quale pulsante è stato premuto ed estrarre il nome dell'azione scelta in base ad esso, preferisco legare un gestore di eventi al pulsante di invio stesso e poi risalire la gerarchia dei nodi per trovare il modulo che il pulsante premuto appartiene a:

// trova il form var $ form = $ (this) .parents ('form'); // estrae il nome dell'azione var nome_azione = $ (this) .attr ('classe'). replace ("dg_action_", "");

Successivamente, aggiungiamo un elemento di input nascosto all'interno dell'elemento del modulo per indicare quale azione viene inviata:

// crea l'input nascosto var action_control = $ (''); // aggiunge al form $ form.append (action_control);

Ciò è necessario perché la funzione non considera il pulsante di invio come una voce di modulo valida. Quindi dobbiamo avere quell'hack in atto durante la serializzazione dei dati del modulo.

action_control.remove ();

"Non dimenticare: la funzione ignora il pulsante di invio, ignorandolo come un altro pezzo di markup spazzatura!"

Invio dei dati del modulo al server

Successivamente, procediamo per ottenere il azione attributo dall'elemento del modulo e aggiungi la stringa "/ ajax"a quell'URL, quindi il metodo saprà che questa è, in realtà, una richiesta AJAX. Successivamente, utilizzeremo il jQuery.post funzione per inviare i dati da elaborare dal controller appropriato, lato server, e quindi intercettare l'evento di risposta con un callback / chiusura registrato:

? var script = $ form.attr ('action') + '/ ajax'; $ .post (script, post_data, function (resp) if (resp.error) alert (resp.error); else switch (action_name) case 'delete': // rimuove le righe cancellate dalla griglia $ form .find ('. dg_check_item: checked'). parents ('tr'). remove (); break; case 'anotherAction': // fa qualcos'altro? break;, 'json')

Si noti che stiamo chiedendo alla risposta di essere codificata come "json" dal momento che stiamo passando quella stringa come quarto argomento del $ .post funzione. Il contenuto del callback che si occupa della risposta del server dovrebbe essere piuttosto semplice da comprendere; determina se c'è un errore e, in tal caso, lo avverte. Altrimenti, indicherà che l'azione è stata elaborata correttamente (in questo caso, se è un'azione "", rimuoviamo le righe relative al ids che sono stati selezionati dall'utente).


Passaggio 4: Controlla tutto o niente!

L'unica cosa che manca ora è la funzionalità di commutazione che ho promesso in precedenza. Dobbiamo registrare una funzione di callback per quando la casella di controllo "Master" - che ha un attributo di classe impostato su "dg_check_toggler"- viene fatto clic su Aggiungi il seguente frammento di codice dopo il precedente:

 $ ('. dg_check_toggler'). click (function () var checkboxes = $ (this) .parents ('table'). find ('. dg_check_item'); if ($ (this) .is (': checked' )) checkboxes.attr ('checked', 'true'); else checkboxes.removeAttr ('checked');)

Quando si fa clic sulla casella di controllo "toggler", se si passa a uno stato "checked", tutte le righe della griglia di dati pertinente verranno verificate simultaneamente; altrimenti tutto sarà deselezionato.


Pensieri finali

Non abbiamo raggiunto la punta dell'iceberg quando si tratta di reti di dati per sistemi di gestione dei contenuti più complessi. Altre caratteristiche che potrebbero rivelarsi utili sono:

  • Ordinamento della griglia di dati in base ai nomi delle colonne
  • Link di impaginazione per navigare nella griglia di dati
  • Modifica / Modifica collegamenti per l'aggiornamento dei dati di una singola riga
  • Meccanismo di ricerca per filtrare i risultati

Grazie per aver letto. Se desideri un tutorial di follow-up, fammi sapere nei commenti!