giovedì 31 maggio 2012

Wicket - Usiamo il DataGrid

Altra richiesta ed altro componente da usare.
Mettiamo che avete una bella form con tanti input e poi vi chiedano di metterli su due o più colonne.

Per gestire questo genere di problematiche, andiamo ad usare il GridView che permette di specificare come riempire una singola cella della griglia per poi specificare il numero di righe o di colonne.

GridView<MyFormItem> gridView = new GridView<MyFormItem>("rows",new ExternalDataProvider<MyFormItem>(elencoDellaRoba))
{
    private static final long serialVersionUID = 1L;
    @Override
    protected void populateItem(Item<MyFormItem> item)
    {
        final MyFormItem r = item.getModelObject();
    final TextField<Integer> na = new TextField<Integer>("braccia", new PropertyModel<Integer>(r, "numeroDiBraccia"));
    item.add(na.setRequired(true));
    item.add(new Label("bracciaLabel",r.getDescrizione()));
    }

    @Override
    protected void populateEmptyItem(Item<Rpp_LatteCrudo> item)
    {
        item.add(new Label("braccia", "").setVisible(false));
        item.add(new Label("bracciaLabel", "").setVisible(false));
    }
};
gridView.setColumns(2);
add(gridView);

Il GridView è facile da usare analogamente al DataView che abbiamo visto più volte. L'unica parte importante riguarda il metodo populateEmptyItem che serve per decidere cosa stampare nel caso la lista non sia interamente divisibile per il numero di colonne che decideremo. Nel nostro caso, avendo scelto due colonne, se ho una lista dispari l'ultima cella sarà riempita con quello indicato in questo metodo.

Nel mio caso stampo due Label vuote e le nascondo.

Andiamo a vedere il codice HTML a cui attaccare questa cosa:

<table cellspacing="0" cellpadding="2" border="1">
    <tr wicket:id="rows">
        <td wicket:id="cols">
         <ul>
             <li><b><span wicket:id="bracciaLabel"></span></b></li>
             <li><input type="text" size="2" maxlength="3" wicket:id="braccia" /></li>
         </ul>
    </td>
    </tr>
</table>

Come vedete occorre solo definire una struttura che lui ripeterà analogamente a quanto succede per il DataView.
L'uso della tabella, in questo caso, è puramente opzionale. L'importante è definire un componente di tipo ROWS ed uno di tipo COLS. Potete mettere degli UL, degli SPAN ecc... e gestirvi tutto tramite CSS. La tabella a mio parere è più facile da gestire e più vicina al concetto di GRIGLIA.

All'interno del "COLS", definiamo cosa e come stampare la roba ricordandoci che è la stessa che dovremo mettere nella populate item.

Anche per questo componente avrete a disposizione il navigator.

martedì 29 maggio 2012

Wicket - Sostituire un Panel da una chiamata Ajax

Come sappiamo i Panel sono l'unità di sviluppo primaria in Wicket e vengono aggiunti alle pagine per generare i contenuti.
I Panel sono molto comodi perché, se fatti bene, sono altamente riutilizzabili.

In questo articolo vediamo velocemente come aggiornare il contenuto di un Panel, legato ad un POJO, in seguito ad un evento AJAX.

SITUAZIONE


Avete una pagina con una form che fa una qualche ricerca e volete stampare l'oggetto che vi ritorna in una scheda (non tabella) sotto la form stessa.

COME PROCEDERE

Per prima cosa create un Pannello (che chiameremo SchedaPanel) ed aggiungetela alla pagina assieme alla form.

Il codice del Panel deve avere la parte della scheda dati nascosta se il POJO è nullo altrimenti prima ancora di submittare la form, vedrete la scheda. Per farlo ci viene in contro l'utile accrocchio di Wicket chiamato ENCLOSURE.

<wicket:panel>
    <fieldset class="informazioni info-ditta" wicket:enclosure="dettagli">
        <legend>Dettagli </legend>
        <div wicket:id="dettagli">
            <ul>
                <li><b>Denominazione</b>: <span wicket:id="denominazione"></span></li>
                <li><b>Forma Giuridica</b>: <span wicket:id="formaGiuridica"></span></li>
                <li><b>Codice Fiscale o PIVA</b>: <span wicket:id="partitaIva"></span></li>
                <li><b>Ragione Sociale</b>: <span wicket:id="codiceFiscale"></span></li>
                <li><b>Indirizzo</b>: <span wicket:id="toponimo"></span> <span wicket:id="via"></span> <span wicket:id="civico"></span> <span wicket:id="cap"> </span><span wicket:id="comune"></span> <span wicket:id="provincia"></span></li>
                <li><b>Telefono</b>: <span wicket:id="telefono"></span></li>
                <li><b>Email Certificata</b>: <span wicket:id="emailCertificata"></span></li>
            </ul>            
        </div>
    </fieldset>
</wicket:panel>

Il codice HTML è molto semplice. Abbiamo un elenco di wicket:id nella quale andremo a mettere i dati del nostro POJO.

Notate però come il FIELDSET sia associato ad un ENCLOSURE chiamato dettagli che guarda caso è uno degli id wicket interni (il DIV).
Le enclosure servono per nascondere l'intero blocco se l'elemento figlio associato è nascosto.

In questo caso quindi se l'oggetto associato al wicket ID dettaglio ha setVisibile a false, allora tutto il fieldset sarà nascosto. E' utile per evitare di dover settare a false tante cose. In pratica il div comanda l'intera visione del blocco.

@Override
protected void onInitialize() {
    super.onInitialize();
    
    dettagli = new WebMarkupContainer("dettagli");
    dettagli.setVisible(false);
    add(dettagli);
    buildSchedaDitta();
}

La initialize del pannello è molto semplice e come vediamo mettiamo a false il div dettagli così da triggerare l'Enclosure.

public void buildSchedaDitta(){
    dettagli.setVisible(true);
    if (this.ditta == null) {
        ditta = new DittaParix();
        dettagli.setVisible(false);
    }
    else {
        dettagli.add(new Label("denominazione",ditta.getDenominazione()));
        dettagli.add(new Label("partitaIva",ditta.getPartitaIva()));
        dettagli.add(new Label("codiceFiscale",ditta.getCodiceFiscale());
        dettagli.add(new Label("formaGiuridica",ditta.getFormaGiuridica()));
        dettagli.add(new Label("toponimo",ditta.getToponimo()));
        dettagli.add(new Label("via",ditta.getVia()));
        dettagli.add(new Label("cap",ditta.getCap()));
        dettagli.add(new Label("civico",ditta.getCivico()));
        dettagli.add(new Label("telefono",ditta.getTelefono()));
        dettagli.add(new Label("emailCertificata",ditta.getEmailCertificata()));
        dettagli.add(new Label("provincia",ditta.getProvincia()));
        dettagli.add(new Label("comune",ditta.getComune()));
        
    }
}

Il metodo di costruzione della scheda è anch'esso intuitivo. Controllo se mi è stata settata la ditta nel qual caso ne inizializzo una a vuota e rimetto a false la visibilità del pannello.
Se invece qualcuno mi ha settata la ditta, questo metodo metterà a true la visibilità e riempirà i dati con le proprietà del POJO.

@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
    SchedaPanel panelTemporaneo = new SchedaPanel("schedaPanel");
    try {
        ditta = QUALCHE_METODO(PIVA);
        if (ditta != null){ 
           panelTemporaneo.setDitta(ditta);
        }
    }
    catch (Exception e){
        fatal("ERRORE: Si è verificato un errore di comunicazione oppure la ditta non è stata trovata");
        target.addComponent(feedbackPanel);
    }
    panelTemporaneo.setOutputMarkupId(true);
    panelOriginale.replaceWith(panelTemporaneo); 
    panelOriginale = panelTemporaneo;
    target.addComponent(panelOriginale);
    target.addComponent(feedbackPanel);
}

La form invece che un Button normale, avrà un AjaxButton perché non dobbiamo riaggiornare tutta la pagina ma solo il panel.
Nella onSubmit lanceremo il nostro metodo di ricerca, riempiremo il nostro POJO ditta e creeremo un nuovo Pannello settandogli la ditta (innescando così la setVisible a true di cui sopra).

La parte interessante arriva adesso. panelOriginale, che era il panel dello stesso tipo aggiunto alla pagina assieme alla form, viene rimpiazzato dalla nuova istanza aggiornata con i dati del Pojo tramite la replaceWith.

A questo punto non dobbiamo fare altro che dire a Wicket di aggiornare sia il panel originale che il feedbackpanel (nel caso vi siano eccezioni) tramite il consueto target.addCompoment.

lunedì 28 maggio 2012

Wicket - Finestra Modale al posto del Confirm

Abbiamo visto nel precedente post come fare una semplice modale che stampa qualcosa.

Oggi vediamo come creare un minimo di interazione di ritorno tra la modale e la pagina che la istanza. Per esempio vogliamo sostituire il classico CONFIRM di javascript con una modale che ci chieda conferma di una operazione.

La costruzione della modale è identica a quella vista nel precedente post. L'unica differenza è la chiamata al Panel che ci mettiamo dentro.
Riportiamo comunque tutta l'inizializzazione:

avvertenzaCancellazione = new ModalWindow("modaleElimina");
avvertenzaCancellazione.setContent(new AvvertenzaEliminaPanel("content",avvertenzaCancellazione,answer));
avvertenzaCancellazione.setTitle("ATTENZIONE");
avvertenzaCancellazione.setResizable(false);
avvertenzaCancellazione.setInitialWidth(500);
avvertenzaCancellazione.setInitialHeight(300);
avvertenzaCancellazione.setWidthUnit("px");
avvertenzaCancellazione.setHeightUnit("px");
avvertenzaCancellazione.setCookieName(null);

avvertenzaCancellazione.setWindowClosedCallback(new ModalWindow.WindowClosedCallback() {
    @Override
    public void onClose(AjaxRequestTarget target) {
        if (answer.isAnswer()) {
           FACCIO QUALCOSA
           target.addComponent(COMPONENTE_DA_AGGIORNARE);
        } 
    }
});
    
add(avvertenzaCancellazione);

La differenza con la precedente MODALE riguarda essenzialmente il Panel che usiamo come contenuto ed ovviamente l'ovveride del metodo onClose su cui arriveremo tra poco.

PANELLO

Il pannello della modale, nel nostro caso AvvertenzaEliminaPanel, riceve nel costruttore la stessa modale ed una variabile chiamata Answer che è praticamente la nostra variabile PONTE tra la pagina e il contenuto della modale.

public class Answer {
    
    private boolean answer;
    
    public void Answer(){
        this.answer = false;
    }

    public boolean isAnswer() {
        return answer;
    }

    public void setAnswer(boolean answer) {
        this.answer = answer;
    }

}

La classe è molto semplice e serve per bypassare il problema del passaggio di informazione. Come sapete Java passa le classi per riferimento ed i tipi primitivi per valore. Non potevo usare una semplice variabile boolean per capire se ho premuto su CONFERMA o ANNULLA perché dentro il panel la modifica non sarebbe stata propagata.

public class AvvertenzaEliminaPanel extends ProtectedPanel {

    private static final long serialVersionUID = 1L;
    
    public AvvertenzaEliminaPanel(String id, final ModalWindow modalWindow, final Answer answer) {
        super(id);
        
        Form yesNoForm = new Form("yesNoForm");
         
        AjaxButton yesButton = new AjaxButton("yesButton", yesNoForm) {
 
            @Override
            protected void onSubmit(AjaxRequestTarget target, Form form) {
                if (target != null) {
                    answer.setAnswer(true);
                    modalWindow.close(target);
                }
            }
        };
 
        AjaxButton noButton = new AjaxButton("noButton", yesNoForm) {
            @Override
            protected void onSubmit(AjaxRequestTarget target, Form form) {
                if (target != null) {
                    answer.setAnswer(false);
                    modalWindow.close(target);
                }
            }
        };
 
        yesNoForm.add(yesButton);
        yesNoForm.add(noButton);
 
        add(yesNoForm);
        
    }


}

Ecco il codice del Panello interno. Abbiamo una FORM (chiamata yesNoForm) ed i due bottoni da premere, uno YES e l'altro NO.

I due bottoni come vedete sono speculari. L'unica cosa che fanno è settare il bolean della variabile ANSWER a true o false in base al bottone stesso ed ovviamente chiudere la modale. E' proprio per chiudere la modale che nel costruttore del pannello devo passare anche la modale. Per poterci lanciare il metodo .close(target).

Riporto anche il codice HTML del pannello.

<wicket:panel>
<div class="avvertenza">
    ATTENZIONE:
    Stai per cancellare qualcosa. Procedere comunque?
    
    <form wicket:id="yesNoForm" action="">
      <div style="margin-top:25px;">
        <table class="yesNo" border="0">
            <tr>
                <td style="width:50%;"><input class="buttonConfirm left" type="submit" wicket:id="yesButton" value="Sì"/></td>
                   <td style="width:50%;"><input class="buttonCancel right" type="submit" wicket:id="noButton" value="No" /></td>
               </tr>
        </table>
      </div>
    </form>
    
</div>
</wicket:panel>

MODALE ON CLOSE

A questo punto quando nel pannello chiudo la modale, viene lanciata il metodo onClose che avevamo visto sopra e che riporto qua sotto:


    @Override
    public void onClose(AjaxRequestTarget target) {
        if (answer.isAnswer()) {
           FACCIO QUALCOSA
           target.addComponent(COMPONENTE_DA_AGGIORNARE);
        } 
    }


Come vedete posso leggere il contenuto della variabile ANSWER che sarà stato aggiornato dal panello (ricordate di dichiararla nella pagina o pannello nella quale state aprendo la modale così da avere lo scope).
In questo caso si entra nell'IF se nel pannello avevo premuto YES (se guardate sopra, la inizializiamo a true). A questo punto posso fare le mie operazioni e riaggiornare eventualmente qualche componente. La onClose è infatti una chiamata Ajax quindi la FORM eventualmente presente non viene submitata quindi se avete, per esempio, bisogno di riaggiornare qualcosa, dovrete aggiungere vari target.addComponent... con le varie componenti. Potete anche riaggiornare tutto il pannello se volete riportarvi ad una situazione base oppure tutta la pagina semplicemente mettendo questo comando:


setResponsePage(PAGINA.class);
Ciao e alla prossima...

giovedì 24 maggio 2012

Wicket - Finestra Modale

Prima o poi te la chiedono la modale e quindi il passo successivo è sperare che non ti diano delle form interne da metterci.

Wicket supporta bene le modali, meno però quelle che hanno dentro delle form da committare perché se submitti una pagina dentro la modale, lui ricarica tutta la pagina originale, non solo la modale.

Per ora quindi vediamo una semplice modale con dentro un pannello che stampa, per esempio, il contenuto di una news.

Nel codice HTML della vostra pagina padre, dovrete avere essenzialmente 2 cose:

  • Un oggetto HTML con WicketID legato alla modale;
  • Qualcosa che triggera l'apertura della stessa (link, icona, bottone ecc...);

<div wicket:id="elencoNews">
        <div class="singleNews">
        <div class="newsTitle" wicket:id="descrizione"></div>
          <div class="newsDate"> Pubblicata il <span wicket:id="dataInizioPubblicazione"></span></div>
        <div>
        <div class="newsContent"><span wicket:id="testo"></span>  <a class="newsCompletaLink" wicket:id="mostraModal"><i>...continua</i></a></div>
        </div>
        <div wicket:id="modale"></div>
    </div>
</div>

Il pezzo sopra rappresenta un estratto del codice HTML di un ipotetico Pannel che mostra un elenco News. Abbiamo visto in altre lezioni come stampare un elenco di informazioni bindate su un certo pezzo di codice. Nel mio caso ho usato il classico DataView con ID elencoNews. Ogni oggetto di tipo News aveva una proprietà Descrizione, DataInizioPubblicazione e Testo, che verranno legate ai wicket-id corrispondenti.

La cosa che ci interessa a noi però, sono le due parti sotto. Abbiamo un LINK con la scritta "...continua" che farà aprire la modale, ed un DIV modale che rappresenta la modale stessa.
Ovviamente le abbiamo messe entrambe dentro il blocco "elencoNews" che nel DataView verrò ripetuto così da avere una modale per ogni news.

//MODALE
    final ModalWindow modale;
    modale = new ModalWindow("modale");
    modale.setContent(new InformazioniNewsPanel("content",news.getId()));
    modale.setTitle("Scheda News");
    modale.setResizable(false);
    modale.setInitialWidth(500);
    modale.setInitialHeight(300);
    modale.setWidthUnit("px"); 
    modale.setHeightUnit("px"); 
    item.add(modale);
    modale.setCookieName(null);

    item.add(new AjaxLink<Void>("mostraModal")
    {
        @Override
        public void onClick(AjaxRequestTarget target)
        {
        modale.show(target);
        }
    });

All'interno della POPULATEITEM del DataView che state implementando, dovrete creare le vostre modali come si vede sopra.
Usiamo l'oggetto ModalWindow di Wicket associato all'ID modale. Lo configuriamo con Titolo, dimensioni ecc...
A questo punto abbiamo due possibilità. O ci mettiamo un contenuto dentro, come un Panel, oppure gli facciamo aprire un'intera pagina. Nel primo caso basta usare il metodo setContent.

modale.setPageCreator(new ModalWindow.PageCreator() 
    {
        @Override
        public Page createPage() 
        {
            return new MyPage("My Page");
        }
    });

Il codice sopra mostra invece come inserire una pagina. In questo caso occorre impostare un PageCreator come nell'esempio.

Infine possiamo vedere come il LINK fa aprire la modale semplicemente lanciando il metodo .show