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à.

Wicket - Link e Parametri

Venendo dal mondo PHP la mia struttura mentale di sviluppo di pagine web era molto ancorata a strutture ben definite.

In PHP per linkare una pagina con dei parametri, occorre semplicemente creare un elemento A in qualche modo con il nostro URL ed i nostri parametri come:

pagina.php?param1=ciao&param2=addio

A questo punto nella pagina.php hai il classico vettore $_GET con dentro ciao e addio.

In Wicket non funziona così.

In Wicket hai una pagina che è mappata su una classe. Tale classe avrà dentro di sé dei parametri che sono quelli che volete settare. Nel nostro caso quindi param1 e param2.

Nella vostra pagina di partenza, avrete un qualche oggetto LINK a cui dovrete implementare un metodo onClick.

public void onClick(String componentId, IModel<OGGETTO> model) {
        setResponsePage(new Pagina(PARAMETRI));
}