martedì 10 luglio 2012

Wicket - Property Column con variazione valore

Diciamo che avete un Property Column legato ad una proprietà di un POJO che viene riempito da DB. Nella  tabella però volete manipolare la resa visiva modificando quindi il valore dentro la proprietà.

Per esempio state stampando un elenco di messaggi e volete stampare solo i primi 100 caratteri del corpo che altrimenti sarebbe troppo lungo.

Per farlo occorre sovrascrivere il metodo PopulateItem della property column come segue:

PropertyColumn colonnaCorpo = new PropertyColumn<Messaggio>(new Model<String>("Corpo"), "corpo"){
    @Override
    public void populateItem(Item<ICellPopulator<Messaggio>> item,
            String componentId, IModel<Messaggio> rowModel) {
        String corpoModel = rowModel.getObject().getCorpo();
        item.add(new Label(componentId,corpoModel.substring(0, Math.min(50,corpoModel.length()))));
    }                
};

Il nostro POJO di partenza si chiama Messaggio e ha dentro una proprietà stringa chiamata corpo.
Sovrascrivendo la populateItem, andiamo a sovrascrivere il modo in cui il componente viene stampato.

item: rappresenta l'oggetto colonna;
componentId: rappresenta l'id del singolo componente (in questo caso dinamico dato che è una cella di una tabella);
rowModel: rappresenta il modello del POJO di cui questo componente sta per stampare una proprietà;

Nell'esempio recuperiamo il testo del corpo dal modello richiamando prima l'oggetto Messaggio dal Modello e quindi richiamando la getCorpo().

A questo punto aggiungo alla item, una Label con lo stesso ID e come stringa la sottostringa del corpo facendo in modo che Java non si incavoli se chiedo più caratteri di quanto la stringa sia effettivamente lunga (leggasi, aggiungo il MIN).

Questo barbatrucco è molto comodo quando dovete ridefinire il modo in cui il componente stampa il valore. Le potenzialità sono multiple. Potete per esempio inserire un TextField dentro la cella, o un Panel con dentro altra roba ecc...

mercoledì 4 luglio 2012

Wicket - Input Text con Autocomplete

Prima o poi ti chiedono anche di fare un input text con i suggerimenti alla Google. Wicket ci permette di farlo in pochi passi.

final AutoCompleteTextField destinatario = new AutoCompleteTextField("destinatario", new PropertyModel<String>(email,"userNameDestinatario"))
 {
      protected Iterator getChoices(String input)
      {
          if (Strings.isEmpty(input))
          {
              return Collections.EMPTY_LIST.iterator();
          }
          
          List<String> elencoFittizio = new ArrayList<String>();
          elencoFittizio.add("Pippo");
          elencoFittizio.add("Pluto");
          elencoFittizio.add("Paperino");
          elencoFittizio.add("Minnie");
          elencoFittizio.add("Paperone");
          elencoFittizio.add("Nonna Papera");
          
          List<String> elencoPulito = new ArrayList<String>();
          
          for (String nome : elencoFittizio){
              nome = nome.toLowerCase();
              String probe = input.toLowerCase();
              if (nome.contains(probe)){
                  elencoPulito.add(nome);
              }
          }
          return  elencoPulito.iterator();
       }
 };

Per farlo usiamo il componente AutoCompleteTextField che aggancieremo ad un tag HTML classico di tipo INPUT TEXT.

Come vedete il costruttore è piuttosto classico. Si definisce il wicket:id (destinatario) e nel mio caso il PropertyModel così da riempire direttamente la proprietà userNameDestinatario dentro l'oggetto email.

A questo punto occorre implementare il metodo che gestisce cosa farci vedere. Come vedete ci viene dato in ingresso una stringa input che rappresenta ciò che l'utente sta inserendo.

Il metodo richiede in uscita un Iterator ovvero l'oggetto che permette a Java di scorrere le Collections ovvero qualsiasi oggetti che rappresenta un elenco di qualcosa.

Se la stringa è vuota, creo una Collections vuota e ritorno l'iterator.

Negli altri casi, devo ritornare l'iterator della lista pulita ovvero della lista risultante dal controllo con l'input inserito.

Nel codice sopra ho mokkato il codice inserendo una lista finta. E' ipotizzabile che qui agganciate un metodo che vada su DB a fare la query e ritorni una lista di cui poi ritornerete l'iterator.

Per farvi capire bene però, nell'esempio ho una lista completa che poi vado a sfoltire sulla base della stringa in ingresso. Nel caso sopra creo una nuova lista con gli elementi della prima che contengono la stringa in input.

Se invece avessi voluto l'aggancio ad un eventuale DB tramite un manager, avrei avuto:


final AutoCompleteTextField destinatario = new AutoCompleteTextField("destinatario", new PropertyModel<String>(email,"userNameDestinatario"))
 {
      protected Iterator getChoices(String input)
      {
          if (Strings.isEmpty(input))
          {
              return Collections.EMPTY_LIST.iterator();
          }
          
          List<String> utentiFiltrati = getManager().cercaUtentiLike(input);
          return  utentiFiltrati.iterator();
       }
 };

Come vedete non mi interessa cosa faccia la cercaUtentiLike, mi interessa solo che mi ritorni una Lista di Stringhe, probabilmente filtrate in fase di interrogazione al DB.

martedì 3 luglio 2012

Wicket - Triggerare il feedback Panel di una Pagina indipendentemente dalla profondità

Un titolo lungo per qualcosa di assolutamente semplice ma potente.

Avevo una situazione di questo tipo:

Pagina con dentro un Panel con dentro una Modale con dentro un Panel. Premendo un tasto di SAVE, dovevo chiudere la modale e mostrare nel feedback panel della pagina, un messaggio di avvertimento all'utente che l'operazione era andata a buon fine.

Per farlo serve un metodo getFeedBackPanel() da dare alla Page e che ritorna l'oggetto.
A questo punto usiamo questo codice:

FeedbackPanel feedbackPadre = ((ProtectedPage) findPage()).getFeedBackPanel();
feedbackPadre.info("Scrivo qualcosa");
target.addComponent(feedbackPadre);

Come vediamo, uso findPage() per trovare un oggetto di tipo Page  (o suo figli) a ritroso nella gerarchia indipendentemente dalla profondità. La devo ovviamente castare alla pagina effettiva che mi aspetto (nel mio caso ProtectedPage) e quindi richiamare il nostro FeedbackPanel.

Poi in maniera classica, ci scrivo qualcosa dentro e quindi lo aggancio all'evento Ajax.

venerdì 8 giugno 2012

Wicket - Modale con FORM

Nell'altro posto abbiamo costruito una modale con un contenuto statico. Purtroppo è una eventualità più remota rispetto ad una modale con dentro del codice da eseguire come nel caso di una FORM.

Il problema delle form dentro le modali riguarda il fatto che a tutti gli effetti tale finestra è una parte della pagina stessa quindi se inserite nella modale una form classica con un submit classico, alla pressione verrà caricata tutta la pagina facendo così scomparire la modale.

Per questo motivo tutte le componenti dentro la modale attiva devono essere sviluppate tramite Ajax. Fortunatamente Wicket ci da una mano.

Costruire il Panel


Il contenuto della modale viene realizzato tramite un pannello che funziona esattamente come quelli che generalmente realizzate. L'unica variazione riguarda la variabile della Finestra Modale che va passata al pannello così che una delle operazioni al suo interno possa chiuderla (il submit con successo per esempio).

final FeedbackPanel feedbackPanel = new FeedbackPanel("feedback");
public EditMicrorganismiLatteCrudo(String idPojo, final ModalWindow modalWindow) {
   super("content");
   add(new MyForm(idPojo,modalWindow));
}

Il costruttore del pannello è molto semplice. In ingresso abbiamo un qualsivoglia id legato ad un POJO a cui legherò la form (giusto per fare qualcosa) e la modale. Credo e aggiungo la form che sarà espressa tramite classe privata ed a cui passo di nuovo la modale.

IMP: Il feedback del pannello va esplicitato sia nel codice HTML che all'interno della classe.

add(new AjaxButton("Salva") {
    private static final long serialVersionUID = 1L;

    @Override
    protected void onSubmit(AjaxRequestTarget target, Form<?> form) {        
        getManager().inserisciPojo(myPojo);
        modalWindow.close(target);
    }

    @Override
    protected void onError(AjaxRequestTarget target, Form<?> form) {
        target.addComponent(feedbackPanel);
    }

});

Come anticipato, gli eventi devono essere tutti Ajax quindi il bottone di submit deve necessariamente esserlo. La onSubmit funziona analogamente ad un comune tasto submit con la differenza che se va a buon fine, si deve chiudere la modale.

L'altra cosa importante riguarda il feedback. In caso di errore occorre esplicitare il comportamento essendo tutto in Ajax. La form ha ovviamente un validatore interno in base a come avete impostato i campi. Se per esempio avete inserito un TextField<Integer> e poi inserite una stringa, la FORM riconosce l'errore e alla pressione del tasto salva, lancerà il metodo onError. A questo punto non vi resta che aggiornare il feedback panel con il solito comando target.addComponent.

IMP: Il feedback deve essere aggiunto all'interno della form in quanto è un elemento figlio dello stesso.

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

martedì 24 aprile 2012

Wicket - Costruire HTML con dati postumi

Eccomi di ritorno con un piccolo trucchetto che farà felici molti.

Wicket per come è fatto richiede che di base le componenti da aggiungere all'oggetto WEB siano messe nel costruttore. Questo però prevede che se quello che devo stampare deve arrivarmi da chi il componente lo istanzia, necessariamente dovrà passarmelo nel costruttore.

Per ovviare a questo problema la soluzione più semplice è utilizzare il metodo che tutti i componenti web di Wicket possiedono ovvero:

onInizialize();

Andiamo a vedere un esempio:


import org.apache.wicket.markup.html.basic.Label;
public class ElencoUPCPanel extends Panel {

    private static final long serialVersionUID = 1L;
    private String test;
    
    public MioPanel(String id) {
        super(id);
    }
    
    @Override
    protected void onInitialize() {    
         super.onInitialize();
         costruisciHTML();
    }

    public void costruisciElenco(){
        add(new Label("elenco_html",test));
    }

    public void setTest(String test){
        this.test = test;
    }
    
}

In questo modo il nostro costruttore potrà essere semplice e potremmo demandare la costruzione delle componenti WEB ad un metodo che verrà chiamato DOPO che avrò impostato i vari parametri della classe. In questo caso la proprietà TEST.

mioPanel = new MIOPanel("dove_metto_il_panel");
mioPanel.setTest("ciao mondo");
add(mioPanel);

L'ipotetica pagina che istanzia il Pannello potrà quindi personalizzarlo settando parametri e aggiungendolo dopo al suo flusso.

NB: La stringa "dove_metto_il_panel" è l'ID Wicket nel codice HTML della pagina nella quale il pannello verrà agganciato.
NB: La stringa "elenco_html" è invece l'ID Wicket nel codice HTML del pannello nella quale verrà messo il contenuto di TEST.

lunedì 23 aprile 2012

Wicket - Link ereditati e non completi

Può capitare di aver bisogno di settare qualche parametro su un LINK o un comportamento su cui però non abbiamo potere perché ereditato da una libreria.

Mi è infatti capitato di ereditare infatti un oggetto imgLinkColumn che creava una colonna per una tabella dove al suo interno c'era una immagine con un link.

L'oggetto funzionava se non fosse che la persona che l'ha creata, non ha parametrizzato l'oggetto dandogli la possibilità, per esempio, di decidere il target del link.

Normalmente se si estende l'oggetto LINK di Wicket, anche ai livelli più bassi di estensione ho comunque a disposizione i metodi della classe originale over compare il setTarget.

In questo caso però la classe originale di partenza era una PropertyColumn che tutto ha fuorché una setTarget visto che il suo scopo è quello di riempirsi con una proprietà associata.

Chi ha esteso questa classe creando appunto la imgLinkColumn ha creato dentro di se il Link ma non lo ha dotato dei metodi del LINK originale.

public AbstractLink createLink(final String componentId, final IModel<T> model) {
        Link<T> link = new Link<T>("link") {
            private static final long serialVersionUID = 1L;

            @Override
            public void onClick() {
                HTMLLinkColumn.this.onClick(componentId, model);
            }
        };
        
        link.setPopupSettings(popupSettings);

        return link;
    }

Come fare quindi a creare un link con target = _blank?

La soluzione è fare Override di questa createLink.


@Override
            public AbstractLink createLink(String componentId,
                    IModel<DittaSearchResult> model) {
                 AbstractLink a = super.createLink(componentId, model);
                 a.add(new AbstractBehavior(){
                    private static final long serialVersionUID = 1L;
                    @Override
                    public void onComponentTag(Component component,
                            ComponentTag tag) {
                         tag.put("target", "_blank");
                    }
                 });
                 return a;
            }

In questo modo stiamo imponendo la chiamata al nostro metodo che alla sua renderizzazione, lancerà la onComponentTag.

La classe AbtrasctBehavior ci serve per poter associare all'oggetto LINK un evento. Purtroppo l'oggetto ci viene dato già fatto dalla superclasse e su quello NON abbiamo potere. Benché infatti l'oggetto LINK di Wicket abbia il metodo onComponentTag (come la maggiorparte degli oggetti Wicket di elementi HTML) non possiamo lanciarlo perché noi siamo prendendo l'oggetto già fatto.

L'unico modo per sovrascrivere quello che ci viene dato, è agganciarci a mano un BEHAVIOR nella quale diciamo di aggiungere il parametro target="_blank" quando l'elemento è renderizzato.

ESTENDERE


L'altro modo per risolvere il problema alla radice prevede di estendere direttamente la classe originale sovrascrivendo così la createLink fallata.

public TargettableImgLinkColumn(IModel<String> displayModel, ResourceReference resourceReference, final String target) {
        super(displayModel, resourceReference);
        this.target = target;
    }

Creiamo quindi un nuovo costruttore che preveda un terzo parametro ovvero il target. Nell'esempio gli altri due parametri sono quelli del costruttore che stiamo ereditando. Uno è il modello e l'altro è il riferimento alla risorsa immagine.

A questo punto abbiamo il nostro target in una proprietà privata interna alla classe. Occorre ora sovrascrivere la createLink che stavamo ereditando.

public AbstractLink createLink(String componentId,IModel<T> model) {
         AbstractLink a = super.createLink(componentId, model);
         a.add(new AbstractBehavior(){
            private static final long serialVersionUID = 1L;
            @Override
            public void onComponentTag(Component component,
                    ComponentTag tag) {
                 tag.put("target", TargettableImgLinkColumn.this.getTarget());
            }
         });
         return a;
    }

 Come vediamo, abbiamo sovrascritto il metodo in maniera analoga a quanto avevamo fatto sopra. L'unica differenza riguarda il fatto che il parametro target lo prendiamo dalla classe stessa.
Da notare che essendo la classe Abstract creata internamente, non può avere scope diretto della proprietà target. Non potevo quindi scrivere semplicemente this.target. La cosa più pulita da fare quindi era prenderla dall'istanza della classe stessa (espressa da this) dentro cui mi stanno dichiarando. Il metodo getTarget() è invece il semplice metodo get della proprietà.