Category: it’s Spring !

SWF: Model Data Validation

spring-webflowDopo aver visto come SWF realizza il mapping tra i dati di un modello e i campi di un form, vediamo ora come creare il meccanismo di validazione che consente di verificare il valore di ciascun campo inserito all’interno di un form. In questo articolo abbiamo visto come viene realizzata il binding dei dati e teniamolo come punto di partenza per iniziare a vedere il meccanismo della validazione.
Riprendiamo il codice sorgente del file di configurazione del flow e la pagina HTML all’interno della quale è presente il form:

<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/webflow
        http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
 
    <var name="flight" class="it.devme.flight.Flight" />
 
    <view-state id="start" view="flight/insertFlightInfo" model="flight">
      <transition on="next" to="confirm" />
    </view-state>
 
    <view-state id="confirm" view="flight/showFlightInfo" />
 
</flow>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
 
<html>
  <head>
    <title>DevMe - SWF Flight info test - </title>
  </head>
 
<body>
  <h1>Flight information </h1>
  <p>Inserisci le informazioni sul volo:</p>
    <form:form id="flightDetails" modelAttribute="flight">
      <table>
        <tr>
          <td>Numero del volo: </td>
         <td><form:input path="number"/></td>
        </tr>
 
        <tr>
          <td>Ora di partenza: </td>
          <td><form:input path="time"/></td>
        </tr>
 
        <tr>
         <td>Gate: </td>
         <td><form:input path="gate"/></td>
        </tr>
 
        <tr>
          <td>Volo in ritardo: </td>
          <td><form:radiobutton path="isDelayed" value="false" />
  	      <form:radiobutton path="isDelayed" value="true" /></td>
        </tr>
 
        <tr>
          <td><input type="submit" name="_eventId" value="next"></td>
        </tr>
    </table>
    </form:form>
  </body>
</html>

Bene. Come già ampiamente discusso, il flow definito dal file XML di sopra è molto semplice. Abbiamo una sola transizione da uno stato all’altro, ovvero si naviga da una pagina ad un altra pagina. Supponiamo ora, di voler validare i dati del form della pagina di partenza, e spostarsi nella pagina di arrivo, solo se non esistono errori di validazione secondo il nostro personale schema di validazione. Il nostro schema, prevede per semplicità che almeno uno dei tre campi presenti nel form sia indicato, e che se indicato il numero del volo questi dovrà essere un valore numero e eventuali caratteri alfabetici verranno rigettati.

Iniziamo con la modifica del file XML di definizione del flow:

<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/webflow
        http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
 
    <var name="flight" class="it.devme.flight.Flight" />
 
    <view-state id="start" view="flight/insertFlightInfo" model="flight">
      <transition on="next" to="confirm" >
          <action method="bindAndValidate" bean="flightAction" />
      </transition>
    </view-state>
 
    <view-state id="confirm" view="flight/showFlightInfo" />
 
</flow>

Il metodo bindAndValidate in sostanza richiama la nostra classe validator, la quale effettua i controlli che andremo a definire. Da notare che il riferimento nel tag action, punta ad un metodo all’interno della nostra classe Action che in realtà non esiste, ma che viene ereditato dalla superclasse FormAction. Definendo all’interno del file XML di definizione del bean il riferimento alla nostra classe Validatore, autormaticamente verrà richiamata all’invocazione del metodo bindAndValidate. Il codice della classe che si occupa di effettuare la validazione è il seguente: 

package it.devme.flight;
 
public class FlightValidator implements Validator {
 
	public boolean supports(Class clazz) {
		return clazz.equals(Flight.class);
	}
 
	public void validate(Object target, Errors errors) {
                Flight flight = (Flight) target;
                if (flight.getNumber()==0 && flight.getTime()==null && flight.getGate.equals("")) {
                    errors.rejectValue("gate", "Attenzione, &egrave; necessario specificare almeno 1 tra: numero del volo, ora di partenza, Gate");
                }
 
                if (errors.getErrorCount()==0) {
                    ValidationUtils.rejectIfEmptyOrWhitespace(errors, "gate", "Il campo gate non pu&ograve; essere vuoto.");
                }
	}
 
}

Vediamo cosa è stato fatto. Intanto osserviamo che FlightValidator implementa la classe Validator, quindi definisce alcuni metodi essenziali che sono supports e validate. Il primo assicura il supporto al Bean indicato al suo interno. Il secondo implmenta la logica di validazione. Nel nostro caso vogliamo che almeno uno dei tre campi venga inserito, e che il campo Gate sia diverso da vuoto. Il primo controllo verifica la prima delle nostre condizioni; il secondo if, verifica prima che non ci siano stati errori in precedenza, e in caso positivo effettua il controllo di validazione sul campo Gate.

Vediamo ora come modifichiamo il file HTML per presentare il messaggio di errore:

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
 
<html>
  <head>
    <title>DevMe - SWF Flight info test - </title>
  </head>
 
<body>
  <h1>Flight information </h1>
  <p>Inserisci le informazioni sul volo:</p>
    <form:form id="flightDetails" modelAttribute="flight">
      <table>
        <tr>
          <td>Numero del volo: </td>
         <td><form:input path="number"/></td>
        </tr>
 
        <tr>
          <td>Ora di partenza: </td>
          <td><form:input path="time"/></td>
        </tr>
 
        <tr>
         <td>Gate: </td>
         <td><form:input path="gate"/></td>
         <td><form:errors path="gate"/></td>
        </tr>
 
        <tr>
          <td>Volo in ritardo: </td>
          <td><form:radiobutton path="isDelayed" value="false" />
  	      <form:radiobutton path="isDelayed" value="true" /></td>
        </tr>
 
        <tr>
          <td><input type="submit" name="_eventId" value="next"></td>
        </tr>
    </table>
    </form:form>
  </body>
</html>

Per semplicità il nostro messaggio di errore comparirà sempre nello stesso posto, ovvero sotto il campo Gate. Così nel primo caso di errore, darà contezza del fatto che almeno uno dei campi di precedenti deve essere indicato, mentre nel secondo caso, sarà indicativo del campo stesso. Come vedete, l’aggiunta del tag <form:errors/> fa in modo di collegare eventuali codici di errori con il campo presentato in pagina.

Alla prossima. Stay tuned!

SWF: Model Data Binding

spring-webflowNell’ultimo articolo su Spring Web Flow ho condensato la maggior parte dei concetti chiave sull’argomento. Mi piacerebbe ora tornare indietro su alcune cose cercando di porre maggiore attenzione. In questo articolo vedremo un pò più da vicino come avviene il binding dei dati in SWF, da un form HTML verso un bean.
In SWF esiste un controller, dello stesso genere di quelli utilizzati in Spring MVC (es. AbstractFormController), che permette di effettuare il binding dei parametri corrispondenti ai campi di un form HTML, quindi presente nella request HTTP verso un command object rappresentato da una semplicissima classe Java Bean. Ad una certa view è associato un certo model object sul quale verranno memorizzati i valori dei parametri del form e affinché avvenga in binding è sufficiente dichiarare l’attributo model dell’elemento <view-state>. L’esempio che segue mostra come deve essere definito il tutto nel contesto SWF affinché avvenga correttamente il binding.

Si comincia col definire la classe del nostro Java Bean, suppioniamo di voler memorizzare le informazioni riguardanti i voli in partenza da un certo aeroporto.

package it.devme.flight;
 
import java.io.Serializable;
import java.sql.Time;
 
public class Flight implements Serializable {
 
        private static final long serialVersionUID = 1L;
 
        private String number;
        private Time time;
	private String gate;
	private boolean isDelayed;
 
	public String getNumber() {
		return number;
	}
	public void setNumber(String number) {
		this.number = number;
	}
	public Time getTime() {
		return time;
	}
	public void setTime(Time time) {
		this.time = time;
	}
	public String getGate() {
		return gate;
	}
	public void setGate(String gate) {
		this.gate = gate;
	}
	public boolean isDelayed() {
		return isDelayed;
	}
	public void setDelayed(boolean isDelayed) {
		this.isDelayed = isDelayed;
	}
 
}

Fin qui niente di più semplice. Procediamo ora con il nostro flow. Il flow consiste di 2 view. La prima pagina raccoglie i dati che verranno memorizzati all’interno dei 4 attributi della classe Flight. Avanzando con il bottone next, la seconda pagina semplicemente mostrerà i dati raccolti.

<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/webflow
        http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
 
    <var name="flight" class="it.devme.flight.Flight" />
 
    <view-state id="start" view="flight/insertFlightInfo" model="flight">
      <transition on="next" to="confirm" />
    </view-state>
 
    <view-state id="confirm" view="flight/showFlightInfo" />
 
</flow>

All’inizio del flow creiamo la variabile di flow flight la quale è un istanza della classe Flight. Fissando questa variabile come attributo model nella start view, diciamo a SWF di utilizzare questo oggetto per effettuare il binding dei dati. Implementando una semplicissima pagina HTML con un form chiudiamo il primo cerchio.

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
 
<html>
  <head>
    <title>DevMe - SWF Flight info test - </title>
  </head>
 
<body>
  <h1>Flight information </h1>
  <p>Inserisci le informazioni sul volo:</p>
    <form:form id="flightDetails" modelAttribute="flight">
      <table>
        <tr>
          <td>Numero del volo: </td>
         <td><form:input path="number"/></td>
        </tr>
 
        <tr>
          <td>Ora di partenza: </td>
          <td><form:input path="time"/></td>
        </tr>
 
        <tr>
         <td>Gate: </td>
         <td><form:input path="gate"/></td>
        </tr>
 
        <tr>
          <td>Volo in ritardo: </td>
          <td><form:radiobutton path="isDelayed" value="false" />
  	      <form:radiobutton path="isDelayed" value="true" /></td>
        </tr>
 
        <tr>
          <td><input type="submit" name="_eventId" value="next"></td>
        </tr>
    </table>
    </form:form>
  </body>
</html>

Cliccando sul Next ci spostiamo verso la seconda view definita all’interno del flow attraverso la transition confirm. All’interno di questa transition i valori dei campi del form vengono inviati in POST e automaticamente ne viene fatto il bind verso l’oggetto flight presente nel flow scope. Successivamente, durante il caricamento della view confirm, vengono eseguite le espressioni EL rispetto al valore dei campi del bean, consentendoci quindi di visualizzarne il valore.

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
 
<html>
  <head>
    <title>Flight Information Details</title>
  </head>
 
<body>
 
  <h1>Flight Information Details</h1>
  <p>Dettagli del volo:</p>
  <form:form modelAttribute="flight" >
 
    <table>
      <tr>
        <td>Numero volo: </td>
        <td>${flight.number}</td>
      </tr>
 
      <tr>
       <td>Orario: </td>
       <td>${flight.time}</td>
      </tr>
 
      <tr>
       <td>Gate: </td>
       <td>${flight.gate}</td>
      </tr>
 
      <tr>
       <td>Ritardo: </td>
       <td>${flight.isDelayed}</td>
      </tr>
 
   </table>
 </form:form>
</body>
</html>

Come vedete è molto semplice. Una piccola nota: se per qualunque ragione non si volesse proseguire durante una transition, al binding dei parametri della request è sufficiente aggiungere l’attributo bind all’interno del tag transition indicando come valore false.

    <view-state id="start" view="flight/insertFlightInfo" model="flight">
      <transition on="next" to="confirm" />
      <transition on="cancel" to="end" bind="false" />
    </view-state>

Nel prossimo articolo parleremo della validazione dei campi di un form. Alla prossima
Stay tuned!

WordPress Themes