Le nostre competenze a vostra disposizione sul blog
Amiamo il nostro lavoro e vogliamo condividerlo con voi! Tenetevi aggiornati su tutte le news e le tecnologie con le quali lavoriamo e di cui potreste avere bisogno! Seguite il nostro blog.
×

Error message

The spam filter installed on this site is currently unavailable. Per site policy, we are unable to accept new submissions until that problem is resolved. Please try resubmitting the form in a couple of minutes.
mattia.minotti's picture

L’introduzione del design pattern MVC (Model-View-Controller) da parte di Sencha nel core dei suoi prodotti risale all’Aprile 2011, con il rilascio della versione 4.0 di ExtJS. Solo recentemente però, esso ha raggiunto una certa maturità grazie a bugfix e miglioramenti che hanno portato alla release 4.2.1 ed all’integrazione della documentazione ufficiale con articoli ed esempi.

Senza dilungarsi troppo sui dettagli con cui questo pattern è stato applicato da Sencha (per chi volesse approfondire consiglio vivamente di consultare la guida ufficiale), di seguito si vogliono dare alcuni suggerimenti che potrebbero facilitare il passaggio dal classico stile di programmazione delle applicazioni Web lato client a questo nuovo approccio.

Progettazione dei Controllers

Uno dei primi aspetti non banali (e per certi versi discutibile) dell’implementazione del pattern MVC proposta da Sencha, è il fatto che i Controller sono classi singleton. Invocando da un Controller il metodo per la creazione di un altro Controller, infatti, il framework procede alla sua creazione e all’invocazione del metodo di inizializzazione solo se esso non esiste già.

this.getController("ExampleController");

Il parametro richiesto dal metodo di cui sopra, è infatti l’id univoco del controller che corrisponde con il nome della classe che lo implementa (e del file che lo contiene). Non è possibile quindi stanziare due controller dello stesso tipo con due id diversi.

Essendo un singleton, ogni Controller deve necessariamente agire in maniera attiva o reattiva su tutte le istanze delle View per cui è stato progettato (ricordiamo infatti che per ogni View possono esistere innumerevoli istanze). Un caso tipico è quello per cui un Controller C deve gestire un insieme di View V1, V2, … Vn. La fase reattiva è gestita tramite il metodo control offerto da tutti i Controller. Esso permette di registrare callback a determinati eventi di tutte le View di un certo tipo o raggiungibili con un certo path, ma generalmente non viene utilizzato per identificare una specifica istanza di una View. Questo significa che all’interno della callback scatenata da un componente interno alla View non si ha un riferimento diretto ad essa ed occorre recuperarlo in qualche modo per identificare di quale delle n istanze si tratta.

Un approccio possibile (che non è comunque quello suggerito) è di salvarsi a livello di Controller i riferimenti a tutte le View che esso gestisce, per poi utilizzarli per effettuare operazioni all’interno della callback. Il problema è: come si riesce a capire qual è la View che ha generato l’evento tra le tante istanze che il Controller sta osservando? E’ chiaro che, così facendo, la gestione si complica e risulta quantomeno intricata.

Una soluzione più lineare, da noi adottata con discreto successo e che quindi ci sentiamo di promuovere, è quella di fare totale affidamento sul meccanismo di ComponentQuery di ExtJS 4. Proviamo a spiegarlo con un esempio pratico:

Ext.define('MyApp.view.V', {
	extend: 'Ext.container.Container',
	alias: 'widget.v',
	initComponent: function() {
		this.items = [
		    {
		    	xtype: 'button',
		    	text: 'do',
		    	action: 'do'
    		},{
    			xtype: 'textfield',
    			name: 'value'
    	    }
    	];
    	this.callParent();
     }
});

Ext.define('MyApp.controller.C', {
     extend: 'Ext.app.Controller',
     init: function() {
    	 this.control({
     		'v button[action=do]': {
     			click: function(button){
     				var v = button.up('v');
     				var value = v.down('textfield[name=value]');
     				value.setValue('Hello World');
     			}
    	    }
    	 });
     }
});

Supponiamo di istanziare un Controller di tipo C ed n View di tipo V. Cerchiamo ora di capire come viene gestito l’evento di pressione di uno dei pulsanti delle View e come viene cambiato il testo del campo che si trova nella stessa View del pulsante (e non nelle altre).

In pratica, l’approccio prevede in generale di non utilizzare mai un riferimento alle viste memorizzato a priori nel Controller, ma di ricavarlo sempre utilizzando il meccanismo di ComponentQuery a partire dal componente che ha generato l’evento. Nell’esempio, a partire dal pulsante che ha generato l’evento, si ottiene il riferimento alla View che lo contiene effettuando un traversing verso l’alto:

var v = button.up('v');

Da esso poi si scende alla ricerca del textfield, a partire dal riferimento della View ottenuto precedentemente:

var value = v.down('textfield[name=value]');

Modellando il Controller in questo modo, senza che esso abbia uno stato in qualche modo legato alle View che gestisce, esso risulta più semplice ed al contempo più generico. Sostanzialmente, seguendo questo pattern, si può progettare un Controller pensandolo per una sola View e nel caso invece essa venga istanziata più volte, il supporto risulta essere in pratica gratuito.

Windows e Component Query

Seguendo l’approccio appena proposto e più in generale le linee guida di Sencha, diventa di fondamentale importanza l’utilizzo delle Component Query. Pur essendo molto potenti e quindi molto comode, esse hanno il limite di operare ricerche sull’alberatura dei componenti che è legata alla struttura del DOM.

In particolare, un problema tipico è quello del recupero del riferimento ad una window che è stata creata da una specifica View dell’applicazione (ad esempio una modale). In ExtJS le window di default vengono inserite nel DOM direttamente dentro all’elemento body, ciò implica che utilizzando le Component Query non c’è modo di raggiungere la View a cui sono collegate (logicamente) a partire dal loro riferimento e vice-versa.

Per risolvere questo problema, finchè Sencha non fornirà meccanismi più strutturati, una possibile soluzione è quella di definire all’interno della window un riferimento diretto al parent, cioè alla View a cui essa deve essere associata. Così facendo, a partire da un componente interno alla window, si può ottenere un riferimento ad essa tramite il normale Component Query, poi da essa si ottiene il riferimento alla parent View su cui si può agire a piacimento.

Di seguito un esempio in cui una View V contiene un pulsante alla cui pressione viene aperta una window W che è collegata alla View tramite il meccanismo detto prima del parent. Tale window ha a sua volta un pulsante alla cui pressione il Controller recupera il riferimento alla View associata tramite il campo parent e agisce su di essa.

Ext.define('MyApp.view.V', {
     extend: 'Ext.container.Container',
     alias: 'widget.v',
     initComponent: function() {
    	this.items = [
    	    {
    	    	xtype: 'button',
    	    	text: 'do',
    	    	action: 'do'
    		},{
    			xtype: 'textfield',
    			name: 'value'
    		}
    	];
    	this.callParent();
     }
});

Ext.define('MyApp.view.W', {
     extend: 'Ext.window.Window',
     alias: 'widget.w',
     initComponent: function() {
    	 this.items = [
    	    {
    	    	xtype: 'button',
    	    	text: 'do',
    	    	action: 'do'
    		}
    	 ];
    	 this.callParent();
     }
});

Ext.define('MyApp.controller.C', {
     extend: 'Ext.app.Controller',

     init: function() {
    	 this.control({
    		 'v button[action=do]': {
    			 click: function(button){
    				 var v = button.up('v');
    				 var w = Ext.create('w');
    				 w.parent = v;
    				 w.show();
    			 }
    		 },
    		 'w button[action=do]': {
    			 click: function(button){
    				 var w = button.up('w');

    				 //this is the jump from the window and its view
    				 var v = w.parent;

    				 var value = v.down('textfield[name=value]');
    				 value.setValue('Hello World');
    				 w.hide();
    			 }
    		 }
    	 });
     }
});

Tale meccanismo, ancora una volta, risulta essere molto utile nel caso l’app preveda più istanze della stessa View, non identificabili direttamente tramite i loro id.

Aggiungi un commento

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.