Come ordinare elementi con jQuery (2/2)
Nel precedente articolo abbiamo visto come il metodo sortable permetta l’ordinamento di una lista di elementi tramite il trascinamento. Ne abbiamo visto però solo l’implementazione “visiva”. Ma se vogliamo che questo ordinamento sia durevole, quindi che rimanga tale anche se ricarichiamo la pagina, dovremo fare un passo in più.
Nel caso pratico, gli elementi li estrarremo da una tabella del database nella quale salveremo l’id, il nome dell’elemento ed il suo ordine.
La modifica dell’ordine dovrà poi essere intercettata ed inviata con una chiamata asincrona ad un file che si occuperà di aggiornare il database.
Come puoi vedere nell’esempio, se modifichi l’ordine, questo rimarrà inalterato anche ricaricando la pagina (a meno che ci sia un altro utente che sta utilizzando l’esempio) in quanto viene salvato nel database così come verrà illustrato in questo articolo.
Creare il database
Creiamo un database e chiamiamalo sortable.
In questo database creiamo una tabella chiamata lista con tre campi: id, item_name e item_order.
Puoi eseguire questa query:
CREATE TABLE `lista` ( `id` int(4) unsigned NOT NULL auto_increment, `item_name` varchar(100) character set utf8 NOT NULL, `item_order` int(4) unsigned NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM;
Ora popoliamo questa tabella con gli elementi che saranno visualizzati:
INSERT INTO `lista` (`id`, `item_name`, `item_order`) VALUES (1, 'Elemento 1', 0), (2, 'Elemento 2', 1), (3, 'Elemento 3', 2), (4, 'Elemento 4', 3), (5, 'Elemento 5', 4);
Ed infine creiamo il file con i parametri di connessione db_config.php:
define("DB_HOST", "localhost"); define("DB_NAME", "sortable"); define("DB_USERNAME", "root"); define("DB_PASSWORD", "*********");
Creare il supporto per le operazioni lato server
Bene, a questo punto possiamo iniziare a sviluppare la classe che si occuperà di gestire le operazioni lato server, la chiameremo sortableSupport.php.
include_once 'db_config.php'; class sortableSupport { private $conn;
Iniziamo con l’includere il file con i parametri di connessione al database e a dichiarare la classe e la proprietà conn che rappresenta la risorsa di connessione.
Scriviamo ora il metodo per la connessione al database sul quale c’è poco da dire:
private function dbConnect() { $this->conn = mysql_connect(DB_HOST,DB_USERNAME,DB_PASSWORD) OR die(); mysql_select_db(DB_NAME,$this->conn) OR die(); }
E dichiariamo questo metodo nel costruttore di classe visto che qualunque operazione che questa classe dovrà svolgere necessita della connessione al database.
public function __construct() { $this->dbConnect(); }
Ora possiamo sviluppare il metodo grazie al quale visualizzeremo la lista degli elementi e che andrà a sostituire l’elenco statico nella pagina index.php:
public function showItem() { $sql = "SELECT * FROM lista ORDER BY item_order"; $res = mysql_query($sql, $this->conn); while($row = mysql_fetch_array($res)) { echo '<li id="item_' . $row['id'] . '">' . $row['item_name'] . '</li>'; } return; }
Come vedi vengono estratti i dati ordinati in base al campo item_order; in seguito andiamo a creare l’elenco in modo dinamico. L’id dell’elemento sarà: item_valoreDellIdPrelevatoDalDatabase. Non ci resta che istanziare la classe. A questo punto il nostro script dovrebbe essere così (non è ancora finito):
include_once 'db_config.php'; class sortableSupport { private $conn; public function __construct() { $this->dbConnect(); } private function dbConnect() { $this->conn = mysql_connect(DB_HOST,DB_USERNAME,DB_PASSWORD) OR die(); mysql_select_db(DB_NAME,$this->conn) OR die(); } public function showItem() { $sql = "SELECT * FROM lista ORDER BY item_order"; $res = mysql_query($sql, $this->conn); while($row = mysql_fetch_array($res)) { echo '<li id="item_' . $row['id'] . '">' . $row['item_name'] . '</li>'; } return; } } $sortableSupport = new sortableSupport();
Ora apriamo il file index.php che abbiamo utilizzato per il precedente articolo. In questo file dovremo sostituire la lista degli elementi con il metodo showItem().
Quindi questa parte di codice:
<ul id="list"> <li id="item_1">Elemento 1</li> <li id="item_2">Elemento 2</li> <li id="item_3">Elemento 3</li> <li id="item_4">Elemento 4</li> <li id="item_5">Elemento 5</li> </ul>
Andrà sostituito con questa:
<ul id="list"> <?php include_once 'sortableSupport.php'; $sortableSupport->showItem(); ?> </ul>
In questo modo la nostra lista verrà inserita dinamicamente.
Individuare il cambiamento di ordine
Ora siamo ad un passaggio cruciale. Dobbiamo in qualche modo individuare il cambiamento di ordine ed inviarlo al server in modo che sia possibile aggiornare il database.
Iniziamo con il riprendere il nostro script jQuery che avevamo lasciato così:
$("#list").sortable({ opacity:0.5, axis: "y" });
A questo punto passeremo come parametro il gestore dell’evento update con il quale definiamo appunto le procedure da svolgere quando la lista viene modificata. Lo faremo come sempre tramite una funzione, in questo modo:
$("#list").sortable({ opacity:0.5, axis: "y", update: function(event,ui){ // cosa fare quando la lista viene modificata } });
Con questa funzione dovremo leggere l’ordine della lista ed inviarla al server con una richiesta ajax.
Per leggere l’ordine degli elementi non dovremo fare altro che serializzare la lista. Ti ricordi dell’articolo che ho scritto sul metodo serialize?
Ebbene, anche sortable dispone del metodo serialize che utilizzeremo semplicemente in questo modo:
var itemOrder = $('#list').sortable('serialize');
Nel precedente articolo avevo insistito sull’importanza del formato dell’id degli elementi della lista: nomeSempreUguale_numeroSempreDiverso
Nel concreto
<li class="item" id="item_1">Elemento 1</li>
Il motivo di questo formato particolare è il seguente:
Serialize si aspetta questo formato e restituirà un array il cui nome sarà quello che c’è prima del trattino, mentre i valori saranno quello che c’è dopo il trattino.
I valori di questo array saranno ordinati nello stesso modo nel quale è ordinata la lista.
Quindi se sposteremo l’elemento 2 prima dell’elemento 1, l’array che ne risulterà sarà così
2,1,3,4,5
O più precisamente, se teniamo conto anche delle chiavi:
item[0] -> 2
item[1] -> 1
item[2] -> 3
item[3] -> 4
item[4] -> 5
Adesso non ci resta che spedire questo array al server con una semplice richiesta ajax:
$.post("order.php", itemOrder);
Naturalmente order.php dobbiamo ancora scriverlo, ma lo faremo presto.
Il nostro codice jQuery è ora pronto, anzi l’intera pagina index.php è pronta e si presenta così:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <script type="text/javascript" src="jquery/jquery-1.4.2.js"></script> <script type="text/javascript" src="jquery/ui/jquery.ui.core.js"></script> <script type="text/javascript" src="jquery/ui/jquery.ui.widget.js"></script> <script type="text/javascript" src="jquery/ui/jquery.ui.mouse.js"></script> <script type="text/javascript" src="jquery/ui/jquery.ui.sortable.js"></script> <link href="screen.css" rel="stylesheet" type="text/css" /> <script type="text/javascript"> $(document).ready(function(){ $("#list").sortable({ opacity:0.5, axis: "y", update: function(event,ui){ var itemOrder = $('#list').sortable('serialize'); $.post("order.php", itemOrder); } }); }); </script> <title>Ordinamento con jQuery | Your Inspiration Web</title> </head> <body> <div id="container"> <ul id="list"> <?php include_once 'sortableSupport.php'; $sortableSupport->showItem(); ?> </ul> </div> </body> </html>
Come aggiornare l’ordinamento nel database?
Ora abbiamo il nostro array in viaggio per il server. Iniziamo con lo sviluppare un nuovo metodo della classe sortableSupport che sia in grado di aggiornare l’ordinamento nel database.
Per farlo scorreremo l’array tramite il costrutto foreach grazie al quale preleveremo la chiave di ciascun elemento (che rappresenterà l’ordine) e il valore che corrisponderà all’id dell’elemento. Dunque per ogni elemento avremo l’id ed il numero di ordinamento. Non dovremo fare altro che aggiornare la tabella, in questo modo:
public function orderItem() { foreach($_POST['item'] as $order => $id) { $sql = "UPDATE lista SET item_order=$order WHERE id=$id"; mysql_query($sql, $this->conn); } }
Come vedi modifichiamo il campo item_order in corrispondenza dell’id.
A questo punto non resta che creare il semplicissimo file order.php, nel quale dovremo unicamente eseguire il metodo appena visto.
include_once 'sortableSupport.php'; $sortableSupport->orderItem();
Per finire ti riporto il codice della classe sortableSupport completa:
include_once 'db_config.php'; class sortableSupport { private $conn; public function __construct() { $this->dbConnect(); } private function dbConnect() { $this->conn = mysql_connect(DB_HOST,DB_USERNAME,DB_PASSWORD) OR die(); mysql_select_db(DB_NAME,$this->conn) OR die(); } public function showItem() { $sql = "SELECT * FROM lista ORDER BY item_order"; $res = mysql_query($sql, $this->conn); while($row = mysql_fetch_array($res)) { echo '<li id="item_' . $row['id'] . '">' . $row['item_name'] . '</li>'; } return; } public function orderItem() { foreach($_POST['item'] as $order => $id) { $sql = "UPDATE lista SET item_order=$order WHERE id=$id"; mysql_query($sql, $this->conn); } } } $sortableSupport = new sortableSupport();
Conclusione
L’articolo è venuto un po’ lungo ma volevo spiegare tutto nei dettagli. Come hai potuto vedere sortable fornisce tutti i supporti per poter implementare in modo semplice l’ordinamento di oggetti.
E tu, hai già utilizzato una funzionalità del genere? In che occasione?
17 commenti
Trackback e pingback
Non ci sono trackback e pingback disponibili per questo articolo
Ciao Maurizio,
Come prima cosa complimenti per l’articolo.
Ti chiedo una piccola informazione il campo item_order perchè non è stato impostato come auto_increment? non hai pensato al fatto che una persona volesse implementare la tabella con nuovi item?
Ciao Simone e grazie.
Per essere impostato come auto increment un campo deve essere chiave.
Per gli inserimenti bisognerà prevedere una query che individua il “MAX order” ed assegna l’ordine successivo al nuovo elemento.
ciao maurizio,
spendido articolo (come al solito…).
Solo un appunto
col foreach messo nella funzione orderItem() vai ad eseguire X numero di query (nell’esempio 5)… ma con tabelle di medie dimensioni (poniamo 100) fare tutte queste query diventa “esosso” per mysql.
non sarebbe stato meglio costruire un unica query in questo modo?
public function orderItem()
{
$sql = "UPDATE lista SET item_order = CASE id";
foreach($_POST['item'] as $order => $id)
{
$sql .= " WHEN $id THEN $order";
}
$sql .= " END WHERE id IN (".implode(',',$_POST['item']).")"
mysql_query($sql, $this->conn);
}
Spero di non aver detto una C******…
Forse sarà una mia fissa ma utilizzando frequentemente hosting gratuiti con limiti di query, a seguito di alcune esperienze tendo a badare più a limitarne il numero piuttosto che limitare le prestazioni…
PS: magari mettendo anche qui e lì degli intval()…
Ciao oly e grazie.
Premetto che risparmiare risore é sempre una buona cosa.
Nel caso specifico.
un sistema come quello presentato si adatta particolarmente a pannelli amministrativi (se vuoi ad esempio permettere agli utenti di spostare degli elementi sulla home page, non utilizzerai certamente il database, lo farai con dei cookie, quindi il problema non si pone).
Se come detto sei lato admin considera che é una sola persona ad utilizzare il sistema e considera ogni quanto lo utilizza (ogni quanto modifichi l’ordine dei widegt del tuo sito?).
Infine, liste formate da centinaia di elementi mal si adattano ad essere ordinate tramite sortable. Prova ad immaginartelo, sarebbe totalmente inutilizzabile :-)
Si potrebbe costriure anche una query piuttosto complicata che vada a spostare unicamente gli elementi che vanno aggiornati. Io ho cercato di presentare la soluzione più semplice in modo che possa essere compresa dal maggior numero di utenti possibile.
Ottimo.
Devo ancora capire certi meccanismi ma per il resto non ho avuto difficoltà particolari.
Un piccolo dettaglio.
Ho inserito nel file css
cursor: move; all’elemento li.item
ma il cursore non si trasforma nella classica figura a 4 frecce tipica, come visto nei file di esempio del tutorial drag and drop quando lo sposto sulle varie voci della lista. Vedo soltanto una linea verticale.
Come mai?
Grazie
come qua ad esempio:
https://www.yourinspirationweb.com/example/maurizio/draganddrop/elimina.html
Ciao Luke, succede perchè devi aggiungere class=”item” agli elemetni
Nel codice qua non c’è ma se guardi il sorgente dell’esempio lì c’è:
https://www.yourinspirationweb.com/example/maurizio/sortable/index2.php
uff mi è uscito male il commento
devi aggiungere class=”item” agli elementi lista
li class=”item” id=”item_1″>Elemento 1</li
ok ok grazie
Articolo chiarissimo e utilissimo.
Se però avessi l’esigenza che il database venga aggiornato non in tempo reale, ma dopo la pressione di un tasto di conferma, come dovrei fare?
Premetto che conosco un po’ meglio il php, mentre su jquery e ajax sono piuttosto a digiuno… ho provato in vari modi e con varie tecniche, ma sembra che la pagina order.php non venga chiamata sempre… forse perché la chiamata è asincrona?
Diciamo che l’idea era sulla document.ready di eseguire solo la serializzazione per visualizzare il risultato nella pagina, mentre la post la farei al click di un button (o al post dellla form), ma sembra che, pur essendo chiamata la funzione, il caricamento della order.php non avvenga. Sto complicando cose semplici? Grazie
Ciao Paolo.
Semplicemente, l’aggiornameno dell’ordine nel database in questo caso non va passato al gestore dell’evento update; Quella parte va tutta rimossa ed implementata all’interno di una funzione che rileva invece l’evento submit
Puoi creare un form contenente unicamente un bottone di invio
poi
$(‘#form’).submit(function() ….
// aggiorna il database, esattamente come abbiamo fatto nel gestore dell’evento update
Grazie per la pronta risposta.
Concettualmente era quello che cercavo di fare, ma devo avere sbagliato qualcosa.
Inoltre mi servirebbero due bottoni, uno per Salva e uno per Annulla che dovrebbe rileggere l’ordinamento dal datbase senza salvare le modifiche fatte, ma non so come distinguerli nel codice jquery.
Comunque, per ora (entrambi i bottoni attivano il codice jquery della submit) ho fatto:
CODICE JQUERY:
$(function(){
$(‘#modulo’).submit(function() {
//alert(“click”);
var itemOrder = $(‘#list’).sortable(‘serialize’);
//alert(itemOrder);
$.post(“order.php”, itemOrder);
});
});
gli alert commentati li avevo messi per vedere se ci passava, perché sembra che la order.php non venga eseguita.
Il form si chiama modulo, come action ho messo la pagina stessa (devo lasciarlo vuoto?), il bottone è definito:
mentre l’altro bottone:
Il codice jquery viene richiamato, ma non succede nulla…
non mi ha preso il codice HTML, come faccio a postarlo?
Alla fine della funzione che rileva il submit, devi sempre mettere
return false;
Altrimenti il modulo verrà processato e non funzionerà come deve.
A quel punto il contenuto dell’action é irrilevante.
Perfetto, così funziona perfettamente!
Veramente semplice e comodo
Grazie
Ciao, scusa se riesumo un post “anzianotto”, ma l’argomento è molto interessante.
Volevo chiedere: se volessi applicare questo sistema ad una tabella e spostare le ?
C’è modo?
Grazie!!