ExtJS Drag and Drop

Nei precedenti articoli abbiamo visto come creare un Web App utilizzando Ext JS ed in particolare come utilizzare alcuni elementi del framework, quali:

Vediamo adesso qualcosa di più complicato, ovvero come utilizzare il Drag and Drop.

Immaginiamo di avere una Web App che gestisca un fantacalcio, immaginiamo di voler permettere agli utenti di impostare la formazione di ogni giornata trascinando i giocatori dalla Grid agli slot presenti su un Campo di Calcio.

Un sempio di ExtJs Drag And Drop

Un sempio di ExtJs Drag And Drop

Per far questo partiamo dalla struttura creata nei precedenti articoli.

Prima di iniziare riduciamo l’altezza del Header e cerchiamo sul web l’immagine di un campo da calcio (questa è quella che ho utilizzato) da posizionare all’interno di resources/images/.

Aggiungiamo adesso una scheda al Tab Panel:


{
    title: 'Formazione',
    itemId: 'campo',
    bodyStyle: "padding: 20px 40px;",
    items: []
}

Inseriremo al suo interno un div avente come sfondo l’immagine del campo. Divideremo poi questo contenitore in 4 colonne e queste in degli “slot” nei quali inserire poi i giocatori trascinandoli dalla rosa (si noti che avremo in totale 11 slot).

Introduciamo quindi un nuovo elemento: il column layout. Il codice di sopra diventa:


{
    title: 'Formazione',
    itemId: 'campo',
    bodyStyle: "padding: 20px 40px;",
    items: [{
        width: 700, 
        height: 462,
        layout:'column',
        defaults: { 
            bodyStyle: 'background:transparent;'
        },
        bodyStyle: "background: url('resources/images/campo.jpg');",
        items: []
    }]
}

Si noti che tutti gli item contenuti all’interno del nostro layout a colonne avranno uno sfondo trasparente di default.

Iniziamo adesso la suddivisione del contenitore in 11 parti, ecco il codice completo:


{
    title: 'Formazione',
    itemId: 'campo',
    id: 'campo',
    bodyStyle: "padding: 20px 40px;",
    items: [{
		width: 700, 
    	height: 462,
        layout:'column',
        defaults: { 
			bodyStyle: 'background:transparent;'
		},
		bodyStyle: "background: url('resources/images/campo.jpg');",
        items: [{
			width: 150,
			height: 462,
			bodyCls: 'player-target',
			html: "<br/><br/><div class='player-div'>Player</div>"
		}, {
			width: 150,
			height: 462,
			layout: 'vbox',
			defaults: { 
				bodyStyle: 'background:transparent;'
			},
			items: [{
				width: 180,
				height: 115,
				bodyCls: 'player-target',
				html: "<div class='player-div'>Player</div>"
			}, {
				width: 180,
				height: 115,
				bodyCls: 'player-target',
				html: "<div class='player-div'>Player</div>"
			}, {
				width: 180,
				height: 115,
				bodyCls: 'player-target',
				html: "<div class='player-div'>Player</div>"
			}, {
				width: 180,
				height: 115,
				bodyCls: 'player-target',
				html: "<div class='player-div'>Player</div>"
			}]
		}, {
			width: 150,
			height: 462,
			layout: 'vbox',
			defaults: { 
				bodyStyle: 'background:transparent;'
			},
			items: [{
				width: 180,
				height: 154,
				bodyCls: 'player-target',
				html: "<div class='player-div'>Player</div>"
			}, {
				width: 180,
				height: 154,
				bodyCls: 'player-target',
				html: "<div class='player-div'>Player</div>"
			}, {
				width: 180,
				height: 154,
				bodyCls: 'player-target',
				html: "<div class='player-div'>Player</div>"
			}]
		}, {
			width: 150,
			height: 462,
			layout: 'vbox',
			defaults: { 
				bodyStyle: 'background:transparent;'
			},
			items: [{
				width: 180,
				height: 154,
				bodyCls: 'player-target',
				html: "<div class='player-div'>Player</div>"
			}, {
				width: 180,
				height: 154,
				bodyCls: 'player-target',
				html: "<div class='player-div'>Player</div>"
			}, {
				width: 180,
				height: 154,
				bodyCls: 'player-target',
				html: "<div class='player-div'>Player</div>"
			}]
		}]
    }]
}

Si noti che i div utilizzano la classe player-div, la sua definizione deve trovarsi nella index.html (o in un file esterno):


<style>
    .player-div{
	background: none repeat scroll 0 0 transparent;
	font-size: medium;
	font-weight: bold;
	padding: 0 10px;
	position: relative;
	text-align: center;
	top: 40%;
	width: 85%;
    }
</style>

Lo scenario è ora terminato, manca però il codice che ci permetterà di draggare i giocatori prelevandoli dalla Grid a fianco e di dropparli in uno degli slot creati.

Prima di tutto sarà necessario rendere draggabili gli elementi presenti nella Grid, per far ciò è necessario aggiungere alcuni parametri nella porzione di codice che si occupa della creazione della Grid, ecco come diventa:


Ext.create('Ext.ux.grid.TransformGrid', 'player-table', {
    stripeRows: true,
    enableDragDrop: true, 
    viewConfig: {
        plugins: {
            ptype: 'gridviewdragdrop',
            dragGroup: 'playerDDGroup'
        }
    }
})

Si noti che è necessario impostare tra le altre cose l’attributo dragGroup, utile a identificare un gruppo di elementi aventi la proprietà di poter essere draggati e poi droppati in delle determinate dropZone. Ciò diventa particolarmente utile quando ci troviamo ad esempio di fronte alla preseza di elementi diversi che possono essere droppati in contenitori distinti.

Dobbiamo infine trasformare gli 11 slot creati in delle dropZone e impostare tutte le azioni da eseguire al lancio di eventi quali onNodeEnter, onNodeDrop, ecc.

Creiamo dunque una funzione che sarà lanciata all’atto della renderizzazione della scheda Formazione, la implementiamo all’interno dell’attributo listener e poi di render:


listeners: {
    render: function(){
        var fieldElements = Ext.get('campo').select('.player-target');
        Ext.each(fieldElements.elements, function(el) {

            var dd = Ext.create('Ext.dd.DropZone', el, {

                ddGroup: 'playerDDGroup',

                getTargetFromEvent: function(e) {
                    return e.getTarget('.player-target');
                },
                onNodeEnter : function(target, dd, e, data){
                    Ext.fly(target).setStyle('background', '#C0C0C0');
                },
                onNodeOut : function(target, dd, e, data){
                    Ext.fly(target).setStyle('background', 'none repeat scroll 0 0 transparent');
                },
                onNodeOver : function(target, dd, e, data){
                    return Ext.dd.DropZone.prototype.dropAllowed;
                },
                onNodeDrop : function(target, dd, e, data){
                    targetEl = Ext.get(target).select('.player-div');
                    targetEl.update(dd.dragData.records[0].data['tcol-1']);                                                    

                    return true;
                }
            });
           });
    }
}

Notiamo che tutti gli 11 slot erano caratterizzati dalla classe player-target, di fatto ogni cella conteneva l’attributo bodyCls: ‘player-target’: ognuno di questi diventa una DropZone capace di accogliere elementi appartenenti al gruppo playerDDGroup (come si evince dal codice).

Gli eventi sono quasi tutti auto-esplicativi, il più importante è probabilmente onNodeDrop: questo è stato utilizzato per cambiare il contenuto del div player-div nel nome del giocatore droppato. L’oggetto dd (ed in particolare dd.dragData) contiene le informazioni relative all’elemento draggato. Il primo elemento dell’array records contiene la riga della Grid (si tratta di un’array perchè in altre circostanze è possibile prevedere il dragging di più righe), mentre l’oggetto data contiene i dati delle tre celle: tcol-0, tcol-1, tcol-2.

Si noti infine la funzione update() necessaria per cambiare il contenuto HTML interno al div.

Leave a Reply