Share |

sabato 30 ottobre 2010

HTML5: implementare soluzioni drag'n'drop

In questo articolo mostrerò quanto è facile implementare le funzionalità di drag'n'drop utilizzando le nuove interfacce che HTML5 mette a disposizione degli sviluppatori Javascript.

Dato che HTML5 non è ancora uno standard non tutti i browsers lo supportano, e tra li stessi che ne implementano le specifiche non tutti le supportano totalmente.

Dimostrerò in questo articolo, testato con FireFox Beta 4.04 e Chrome v.7, che seppur entrambi i browsers implementino i gestori degli eventi scatenati durante le varie fasi del drag'n'drop, solo FF implementa correttamente l'interfaccia DataTransfer, che fondamentalmente è una hashmap associata agli eventi, per poter trasportare informazioni supplementari all'oggetto trascinato e depositato altrove all'interno della pagina.

Questo è il motivo per il quale l'articolo originale da cui ho preso spunto, come dichiarato dallo stesso publisher, funziona correttamente solo su FF e non su Chrome.

Nel caso non si abbia molta dimestichezza con il drag'n'drop e le API da utilizzare è assolutamente consigliata la lettura del suddetto articolo.

Successivamente mostrerò come ho dovuto procedere per aggirare il problema.

Nota:

L'esempio consiste nel poter creare la propria formazione della nazionale Italiana di calcio trascinando le immagini dei giocatori dalla panchina al campo di gioco, che è diviso in 4 zone: area di porta, difesa, centrocampo ed attacco.
Per semplificare la comprensione dell'articolo e focalizzare l'attenzione solo sulla gestione degli eventi, che vengono scatenati durante le varie fasi(click, trascinamento, rilascio ecc..), ho ridotto gran parte del codice originale grazie all'utilizzo degli Standard Event Attribute che alcuni elementi HTML posseggono. In aggiunta all'originale, oltre ad una migliore veste grafica e la possibilità di poter scegliere tra molti dei campioni della nazionale Italiana campione del Mondo, è presente l'implementazione di una banale classe per wrappare il suddetto elemento DataTransfer e rendere lo script cross-browsers e sono gestiti con alert i posizionamenti errati dei giocatori(es: difensore al posto di un attacccante). Detto questo, rimane pur sempre un prototipo valido per una buona introduzione alla gestione del drag'n'drop con le nuove API HTML5.

Tutto il codice sorgente presente in questo articolo è comodamente scaricabile in formato .zip a questo link.

Il codice HTML

Analizzando bene il codice HTML sarà possibile scoprire molti meccanismi che vengono utilizzati successivamente dalla parte Javascript per maneggiare le operazioni di drag'n'drop ed eseguire controlli sulla posizione in campo dei vari giocatori.

Il BODY è composto da due DIV: la panchina ed il campo da gioco.
All'interno della panchina sono presenti tutte le immagini dei giocatori, mentre il campo è composto a sua volta da 4 DIV, uno per ogni zona del campo sopra elencata.

Di seguito un estratto del DIV#panchina:

<div id="panchina" ondragenter="return onDrgEntr(event);" 
      ondragover="return onDrgOvr(event);" ondrop="return onDrp(event);"> 
  <img src="italia.png" draggable="false" class="no-drag"> 
 
  <img src="buffon.jpg" id="Buffon" title="Buffon" class="goalkeeper" 
        ondragstart="return onDrgStrt(this,event);"> 
  <img src="amelia.jpg" id="Amelia" title="Amelia" class="goalkeeper" 
        ondragstart="return onDrgStrt(this,event);"> 
  <img src="peruzzi.jpg" id="Peruzzi" title="Peruzzi" class="goalkeeper" 
        ondragstart="return onDrgStrt(this,event);"> 
 
      ..................................
</div> 

Come è possibile notare sul DIV#panchina sono presenti gli event attributes per poter richiamare i gestori degli eventi dragenter, dragover e drop, i quali ho settato rispettivamente con i metodi javascript onDrgEntr(event), onDrgOvr(event) e onDrp(event).

Su ogni immagine è presente, oltre all'attributo per la gestione delle operazioni di trascinamento soddisfate grazie al metodo onDrgStrt(elemento,evento), il riferimento alla classe CSS per disegnare il componente: attenzione, la classe è solo un espediente per poter successivamente controllare se la posizione scelta per il giocatore in campo è la più indicata.

Ogni portiere ha la classe CSS goalkeeper, i difensori defender, i centrocampisti middlefield e gli attaccanti striker.

Ora qualcosa riguardo il campo da gioco, ovvero il DIV#field, di cui segue il codice HTML:

<div id="field" 
      ondragenter="return onDrgEntr(event)" 
      ondragover="return onDrgOvr(event);" 
      ondrop="return onDrp(event);"> 
  <div id="field-goalkeeper" class="zone"></div>
  <div id="field-defender" class="zone"></div>
  <div id="field-middlefield" class="zone"></div>
  <div id="field-striker" class="zone"></div> 
</div> 

Concettualmente il DIV#field deve comportarsi come il DIV#panchina: deve poter accogliere dinamicamente nuovi elementi, o child, e deve potersene anche privare; quindi sono presenti li stessi event attributes già visti per la panchina.

In questo articolo è tralasciata la definizione degli stili CSS, che sono presenti all'interno del file .zip scaricabile.

Prima di analizzare il codice Javascript che è stato associato ad ogni event attribute degli elementi HTML sopra trattati è necessario conoscere la wrapper class DataTransfer in quanto utilizzata dagli event handlers di cui si parlerà.

La wrapper class DataTransfer

Per poter aggirare la mancanza del supporto all'interfaccia event.dataTransfer dell'attuale versione 7 di Chrome ho creato un oggettino che espone i metodi base dell'interfaccia originale e che ha lo stesso modo di utilizzo. Questo oggetto ha uno scope globale essendo dichiarato al massimo livello nella pagina.
Segue il codice sorgente del mio wrapper:

/**
 * Wrapper class per risolvere la mancanza 
 * di supporto all'oggetto event.dataTransfer 
 * caratteristica del browser Chrome v7.
 * Si tratta un array da utilizzare come una HashMap
 * tramite coppie di chiave/valore.
 */
function DataTransfer(){
  var m = new Array();
 
  this.getData = function(key){
    return m[key];
  };
 
  this.setData = function(key, value){
    m[key] = value;
  };
}
var dataTransfer = new DataTransfer();

Si tratta proprio di un banale oggettino che si comporta come una hashmap: attribuisce ad una chiave un valore, che restituisce solo con la chiave abbinata.

Ora il codice Javascript implementato per la gestione degli eventi e la realizzazione delle funzionalità di base che il drag'n'drop richiede.

Gestire gli eventi dragEnter & dragOver

Questi eventi si verificano quando il mouse si trova su un componente che è pronto ad accettare nuovi elementi (evento dragenter).Nel momento in cui avviene il drop questo evento muore e si scatena dragover, che determina il risultato da restituire all'utente: se l'evento non viene interrotto o cancellato solitamente questo non fa niente di particolare.

/** 
 * Gestore dell'evento dragenter sul target. 
 * Per evitare che il drop avvenga in altre zone della pagina 
 * attraversate dal mouse durante il trascinamento di un'oggetto
 * viene eseguita una chiamata al metodo event.preventDefault().
 */
function onDrgEntr(event) {
   event.preventDefault();
   return true;
}
 
/** 
 * Gestore dell'evento dragover sul target. 
 */
function onDrgOvr(event){
  return false;
}

Gestire l'evento dragStart

Questo evento si scatena dopo aver cliccato su un componente della pagina per poterlo trascinare mantenendo il bottone del mouse premuto. Si può affermare che è l'evento che da il via al drag'n'drop.

/** 
 * Gestore dell'evento ondragstart sull'oggetto trascinato. 
 * Riceve il riferimento all'oggetto trascinato 
 * per poter settare dei valori utili all'applicazione.
 */
function onDrgStrt(el,event){
  dataTransfer.setData("player", el.getAttribute('title'));
  dataTransfer.setData("role", el.getAttribute('class'));
  return true;
}

Come si nota, la firma del metodo prevede un evento come secondo parametro del quale però all'interno del metodo stesso non viene fatto alcun utilizzo.
Questo è dovuto alla defezione di Chrome v.7 di cui sopra si è parlato: in questo caso, anziché utilizzare il mio wrapper, si sarebbe dovuto utilizzare l'istanza dell'oggetto dataTransfer associato all'evento. Il codice corretto sarebbe dovuto essere:

function onDrgStrt(el,event){
  event.dataTransfer.setData("player", el.getAttribute('title'));
  event.dataTransfer.setData("role", el.getAttribute('class'));
  return true;
}

Rimane solo la gestione dell'ultima fase che conclude un drag'n'drop: il drop, o il rilascio del componente trascinato.

Gestire l'evento drop

Questo è l'evento che solitamente contiene la maggior parte della logica e dei controlli applicativi.

In questa circostanza, l'event handler associato è il metodo onDrp(event):
al suo interno sono effettuati controlli sia sui targets depositari dell'oggetto droppato, per evitare che venga depositato su un componente di gioco errato, sia sui componenti draggati, per esempio avvisare immediatamente l'utente di aver posizionato un difensore in attacco o centrocampo.

/** 
 * Gestore dell'evento ondrop sul target. 
 */
function onDrp(event){
  // Contiene il valore dell'attributo title, identico ad id,
  // del giocatore draggato.
  var val = dataTransfer.getData('player');

  // Rappresenta il contenitore nel quale si è lasciato cadere
  // il giocatore trascinato.
   var trgt = event.target;

  // Evito di depositare un giocatore su un'altro giocatore.
  if(trgt.tagName.toLowerCase() == 'img'){
    document.getElementById('panchina').appendChild(trgt);
    dataTransfer.setData('player','');
    return false;
  }
 
  // Controllo della zona, con eventuale avviso, 
  // e del riposizionamento in panchina. 
  if(trgt.id.index0f(document.getElementById(val).className) == -1 
      && trgt.id != 'panchina')
    alert("Il giocatore non ricopre il suo ruolo.");
 
  // Deposito il giocatore in una porzione di campo.
  trgt.appendChild(document.getElementById(val));
  dataTransfer.setData('player','');
  return false;
}

Come è facile intuire, implementare applicazioni client che fanno uso del drag'n'drop con HTML5 è molto più semplice, chiaro e rapido rispetto a ciò che fino a ieri si era costretti ad inventare; e se si considera che il nuovo linguaggio è ancora molto lontano dal divenire uno standard, è naturale provare la speranza che le cose potrebbero diventare ancore più semplici.

L'esempio proposto ha ampie possibilità di miglioramento, sia a livello di codice sia nel numero di funzionalità e controlli, per esempio non dovrebbe consentire di avere più di 11 giocatori schierati e dovrebbe vietare la presenza di 2 portieri nello stesso ruolo.

Alla prossima,
MA

domenica 10 ottobre 2010

Import-Export di un server IBM WAS 6.x/7.x

Questo articolo tecnico è redatto per documentare e facilitare i passaggi necessari per la buona riuscita dell'export, e dell'import, di un profilo server WebSphere Application Server, appunto WAS, 6.x/7.x.

Questo documento si riferisce esclusivamente all'utilizzo degli script batch DOS forniti in allegato e scaricabili anche in formato .zip:

  • ExportWAS6.1.bat;
  • ImportWAS6.1.bat;

Gli archivi di profili esportati, e da importare, hanno estensione .car(Configuration Archive).

Nota:

Gli script riportati in questo articolo fanno riferimento a risorse e file esistenti e reperibili esclusivamente con la configurazione del mio pc di sviluppo. Quindi è scontato che, per funzionare correttamente su un pc diverso dal mio, gli script dovranno essere modificati e personalizzati in base alla configurazione di sistema del proprio computer.

Di seguito sono elencati i requisiti di sistema che si riferiscono al mio ambiente di lavoro Windows XP Professional, in modo da capire dove e come modificare i batch per soddisfare le tue esigenze.

Specifiche di ambiente

Entrambi gli script, per l'import e per l'export di server WAS, fanno riferimento alle risorse di seguito elencate:

  • L'installazione dell'ambiente RAD si trovi all'indirizzo:
    C:\ibm\ ;
  • Esista il profilo WAS di default, rappresentato da una cartella
    di nome was61profile1 posizionata all'indirizzo:
    C:\ibm\runtimes\base_v61\profiles\ ;
  • Nel momento in cui avvengono le operazioni di import o di export è necessario avere un'istallazione funzionante dell'ambiente IBM RAD completa di un'istanza server WAS 6.x/7.x avviata.

Export di un profilo server WAS 6.x/7.x

Il batch DOS ExportWAS6.1.bat esegue l'export di un profilo WAS 6.x/7.x .
Il profilo da esportare sarà creato in C:\ServerWAS61.car .

cd \
CALL C:\ibm\runtimes\base_v61\profiles\was61profile1\bin\wsadmin.bat 
    -c "$AdminTask exportWasprofile {-archive C:\ServerWAS61.car}"
exit

Import di un profilo server WAS 6.x/7.x

Il batch DOS ImportWAS6.1.bat esegue l'import di un profilo WAS 6.x/7.x .
Il profilo da importare deve essere presente in C:\ServerWAS61.car .

cd \
CALL C:\ibm\runtimes\base_v61\profiles\was61profile1\bin\wsadmin.bat 
    -c "$AdminTask importWasprofile {-archive C:\ServerWAS61.car}" 
    -c "$AdminConfig save"
exit

In generale, entrambi i batch fanno uso dell'eseguibile wsadmin.bat installato con la release di RAD su cui gira l'istanza WAS su cui lavorare. Ogni batch, essendo specifico, passa all'eseguibile IBM alcuni parametri e comandi differenti dall'altro.

Avendo testato entrambi i batch ho notato che la fase di export è molto più rapida di quella di import, che pare essere interminabile.

Alla prossima,
MA.

martedì 5 ottobre 2010

PHP: Insert e Update di records con PDO

In questo articolo PHP verrà mostrata una tecnica standard per l'inserimento e la modifica di records che si fonda sull'utilizzo del componente PDO.

Questo articolo va a completare l'overview che ho voluto dedicare all'argomento.

Se non conosci PDO, per poter seguire questa discussione ti consiglio una lettura veloce ai seguenti post:

Prima di analizzare gli script di esempio, è necessario che ti mostri la base dati scelta.

La tabella Dipendenti

Come base dati di esempio per il proseguio dell'articolo sarà utilizzata una tabella fittizia di possibili dipendenti, di una qualunque società, residente in un database MySQL.

La tabella scelta si chiama Dipendenti, è composta di soli campi stringa ed è così strutturata:

Tabella Dipendenti
matrnomecognomenascitacf
000AAA1MirkoAgrati06/12/1975zzzaaaag7887dhsg
000AAA2PippoRossi26/11/1990z44rsaag5811dhsk
000AA02LucaBianchi11/03/1980j77ssrtm3321azpp

Nota

Entrambi gli script che seguono danno per scontata la conoscenza della corretta struttura e sintassi di un DSN (o Data Source Name) e la conoscenza delle differenze che vi sono tra l'utilizzo del metodo query e dei metodi prepare ed execute.

Tutti questi argomenti sono stati affrontati e spiegati negli articoli suggeriti nell'introduzione di questo post.

Inserire nuovi records in tabella

Per inserire nuovi records in una tabella di un DB relazionale qualunque è utilizzato il comando SQL INSERT.

Il componente PDO consente di eseguire questo tipo di istruzione SQL sia attraverso il comando PDO->query() sia attraverso l'accoppiata PDO->prepare() e PDO->execute(); in questo articolo esporrò solo la tecnica che fa uso degli ultimi due metodi.

<?php
$matr = 'SW4080DD';
$name = 'Pino';
$sname = 'Petruzzo';
$birthday = '11/12/1974';
$cf = 'PNPTRZ1710180009';

try{
  $dbh = new PDO($dsn, $user, $password);
  $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  $sql = 'INSERT INTO dipendenti'
      . ' (matr, nome, cognome, nascita, cf)' 
      . ' VALUES (:matr, :name, :sname, :birthday, :cf)';
  $stmt = $dbh->prepare($sql);
  $stmt->bindParam(':matr', $matr);
  $stmt->bindParam(':name', $name);
  $stmt->bindParam(':sname', $sname);
  $stmt->bindParam(':birthday', $birthday);
  $stmt->bindParam(':cf', $cf);
  $stmt->execute();
}
catch (PDOException $e) {
  echo 'Si è verificata una PDO Exception.';
  echo 'SQL Query: ', $sql;
  echo 'Errore: ' . $e->getMessage();
}
?>

Al termine dell'esecuzione di questo script, se non sono state sollevate PDOException, la tabella Dipendenti figurerà come segue:

Tabella Dipendenti
matrnomecognomenascitacf
000AAA1MirkoAgrati06/12/1975zzzaaaag7887dhsg
000AAA2PippoRossi26/11/1990z44rsaag5811dhsk
000AA02LucaBianchi11/03/1980j77ssrtm3321azpp
SW4080DDPinoPetruzzo11/12/1974PNPTRZ1710180009

Aggiornare i dati presenti in tabella

Per modificare records presenti in tabella si deve fare uso del comando SQL UPDATE.

Tramite un'istruzione di update è possibile modificare un solo record come pure un set di records diversi: ciò che filtra i dati da aggiornare è la clausola di WHERE.

Nell'esempio scelto verrà modificato il nome del dipendente Petruzzo che si chiama Piero, ma per errore in tabella è stato inserito Pino.

<?php
$matr = 'SW4080DD';
$name = 'Piero';

try{
  $dbh = new PDO($dsn, $user, $password);
  $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  $sql = 'UPDATE dipendenti'
      . ' SET nome = :name' 
      . ' WHERE matr = :matr';
  $stmt = $dbh->prepare($sql);
  $stmt->bindParam(':name', $name);
  $stmt->bindParam(':matr', $matr);
  $stmt->execute();
}
catch (PDOException $e) {
  echo 'Si è verificata una PDO Exception.';
  echo 'SQL Query: ', $sql;
  echo 'Errore: ' . $e->getMessage();
}
?>

Finalmente il dipendente Petruzzo ha ri-ottenuto il suo vero nome: Piero!

Come si nota, la vera differenza tra i due script sta nell'istruzione SQL e non nel modo in cui si risolve il problema!

Tra l'altro, utilizzando i metodi PDO->prepare() e PDO->execute() siamo liberi di non preoccuparci del controllo delle stringhe: questa tecnica è essenziale per evitare problemi dovuti ad attacchi portati tramite SQL Injection.

Per chi come me sviluppa molto in ambiente Java avrà sicuramente notato una certa assomiglianza tra PDO e JDBC.

Alla prossima,
MA.

lunedì 4 ottobre 2010

Aperte le iscrizioni alla gtConference 2010

Il network gt, con l’organizzazione dell’Associazione GT Lab, presenta a Milano la seconda gtConference.

L’evento, ad ingresso gratuito con prenotazione online, si terrà l’8 e 9 Ottobre presso l’Università Bocconi (Aula Zappa, via Roberto Sarfatti 25).

Al culmine di un Tour di eventi online ed offline che hanno già visto più di 1000 presenze, la gtConference arriva a Milano.

L’evento è diviso in due giornate ed offrono un momento formativo importante su argomenti come Google, Facebook e Web Marketing.

Relatori

  • Giorgio Taverniti
  • Francesco Tinti
  • Enrico Altavilla
  • Cesarino Morellato
  • Roberto Marmo
  • Antonio Borri
  • Michela Simoncini

Programma

  • Enrico Altavilla:
    Spam test: cosa si impara sul SEO, sul social e su Google facendo i bimbi cattivi
  • Michela Simoncini:
    Pagine FB, Twitter, Foursquare, etica e buon senso.
  • Cesarino Morellato:
    Misurare i Social Network
  • Francesco Tinti:
    PPC: Pay per Click

Io mi sono appena iscritto! Ci vediamo lì! :)

Alla prossima,
MA