09
Ene
13

La validación mola mogollón (también)

Wicket dispone de gran cantidad de validadores predefinidos permitiendo además definir validadores propios.
Los validadores devuelven al componente sobre el que aplican, mensajes acerca del estado de la validación realizada. La idea es crear una extensión de Label que se pueda asociar a un componente de formulario y que muestra el resultado de la validación junto a este, con un comportamiento similar al de FeedbackPanel.
Este componente se activará ajaxmente, por defecto con el evento onchange del componente asociado, aunque admitirá otros eventos en el constructor. Para permitirlo hay que activar la generación del id, setOutputMarkupId(true), tanto en el componente como en la etiqueta.
Para obtener el mensaje de validación, simplemente se establecerá como modelo de la etiqueta un PropertyModel, usando como expresión de la propiedad «feedbackMessage.message» y como objeto del modelo el propio componente asociado.
Como último paso, para se añade al componente el comportamiento de actualización mediante Ajax, forzando a que se refresquen ambos componentes mostrando los modelos actualizados. En este caso se refresca el contenido de los componentes tanto en caso de éxito como de error (que es lo que ocurrirá cuando no se cumplan las condiciones del validador, en el caso del ejemplo, cuando se introduzca una dirección de email no válida)

-FeedbackLabel.java

import java.io.Serializable;

import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.model.PropertyModel;

@SuppressWarnings({"rawtypes","serial"})
public class FeedbackLabel extends Label {
    private static final long serialVersionUID = 1L;
    
    public FeedbackLabel(String id, final FormComponent component) {
    	this(id, component, "onchange");
    }
    
    public FeedbackLabel(String id, final FormComponent component, final String event) {
        super(id);
        
        this.setOutputMarkupId(true);
        this.setDefaultModel(
            new PropertyModel<Serializable>(component, "feedbackMessage.message"));
        
        component.setOutputMarkupId(true);
        component.add(new AjaxFormComponentUpdatingBehavior(event) {
            protected void onUpdate(AjaxRequestTarget target) {
                target.add(component);
                target.add(FeedbackLabel.this);
            }
            
            protected void onError(AjaxRequestTarget target, RuntimeException e) {
                target.add(component);
                target.add(FeedbackLabel.this);
            }
        });
    }
}

-Index.java

import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.form.EmailTextField;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.model.CompoundPropertyModel;
import org.wicketstuff.annotation.mount.MountPath;

@MountPath("/index")
@SuppressWarnings("serial")
public class Index extends WebPage {
    private static final long serialVersionUID = 1L;

    public Index() {
        add(new TestForm("form"));
    }

    private class TestForm extends Form<Test>{
        private EmailTextField textField;
        private FeedbackLabel textFieldMsg;
		
        public TestForm(String id) {
            super(id);
			
            setModel(new CompoundPropertyModel<Test>(new Test()));
			
            add(textField = new EmailTextField("test"));
            add(textFieldMsg = new FeedbackLabel("testMsg", textField));
        }		
    }
}

-Index.html

<!DOCTYPE html>
<html xmlns:wicket="http://wicket.apache.org">
    <head>
        <meta charset="UTF-8">
        <title>Insert title here</title>
    </head>
    <body>
        <form wicket:id="form">
            <input wicket:id="test" type="email" />	
            <label wicket:id="testMsg">Default</label>
        </form>
    </body>
</html>
07
Ene
13

La internacionalización mola mogollón

Hoy toca Wicket, que es un framework chachi.

La idea es añadir un DropDownChoice con el que poder seleccionar la localización a usar en la sesión actual y mantenerla como una cookie.

En la clase Application (que extiende de WebApplication) se definen las localizaciones que se mostrarán:

	private final static Map<String,Locale> LOCALE_MAP = new HashMap<String, Locale>();
	private final static List<Locale> LOCALE_LIST = new ArrayList<Locale>();
	static{
		 Locale es = new Locale("es");
		 Locale en = Locale.ENGLISH;
		 
		 LOCALE_MAP.put(es.getLanguage(), es);
		 LOCALE_MAP.put(en.getLanguage(), en);
		 
		 LOCALE_LIST.add(es);
		 LOCALE_LIST.add(en);
	}
	
	public Locale getLocaleByLanguage(String lang){
		return LOCALE_MAP.get(lang);
	}
	
	public List<Locale> getLocales(){
		return LOCALE_LIST;
	}

así como los métodos para persistir y recuperar la selección como cookie. En ambos se accede a la cookie desde el RequestCycle disponible en cada Component de Wicket.

	public Locale getLanguage(RequestCycle requestCycle) {		
		WebRequest request = (WebRequest)requestCycle.getRequest();
		
		Cookie language = request.getCookie("language");
		if(language != null){
			String lang = language.getValue();
			return getLocaleByLanguage(lang);
		}
		return Locale.getDefault();
	}

	public void setLanguage(Locale l, RequestCycle requestCycle) {
		String path = getServletContext().getContextPath();
		
		Cookie language = new Cookie("language", l.getLanguage());
		language.setPath(path);
		
		WebResponse response = (WebResponse)requestCycle.getResponse();
		response.addCookie(language);
	}

Lo siguiente será extender DropDownChoice dándole la funcionalidad. En el constructor del componente se cargarán las opciones a mostrar, definidas en la clase Application anteriormente. Como modelo del componente se usará un PropertyModel, empleando como expresion de la propiedad «session.locale» y como objeto del modelo el propio componente (que define un método de acceso a la sesión). Con esto se consigue que el valor seleccionado (inicialmente el obtenido de la cookie enviada o el predefinido en el servidor, por defecto) se establezca como localización de la sesión actual, y adicionalmente con la llamada al método onSelectionChanged se persiste la selección en el cliente en forma de cookie.

import static org.orzowei.blog.Application.getApp;

import java.util.Locale;

import org.apache.commons.lang.StringUtils;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.IChoiceRenderer;
import org.apache.wicket.model.PropertyModel;

public class DropDownLocale extends DropDownChoice<Locale> {
	private static final long serialVersionUID = 1L;

	public DropDownLocale(String id) {		
		super(id);
		setModel(new PropertyModel<Locale>(this, "session.locale"));
		setChoices(getApp().getLocales());
		setChoiceRenderer(new LocaleChoiceRenderer());		
		setModelObject(getApp().getLanguage(getRequestCycle()));
	}

	@Override
	protected void onSelectionChanged(Locale locale) {
		getApp().setLanguage(locale, getRequestCycle());
	}

	@Override
	protected boolean wantOnSelectionChangedNotifications() {
		return true;
	}

	private class LocaleChoiceRenderer implements IChoiceRenderer<Locale> {
		private static final long serialVersionUID = 1L;

		@Override
		public Object getDisplayValue(Locale locale) {
			return StringUtils.capitalize(
				locale.getDisplayLanguage(locale).toString());
		}

		@Override
		public String getIdValue(Locale locale, int i) {
			return locale.getLanguage();
		}
	}
}

Ya se puede añadir el componente a una página simple, en la que se mostrará el enlace con mensaje definido en el fichero de propiedades correspondiente a la localización seleccionada.

Index.java

import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.link.Link;
import org.wicketstuff.annotation.mount.MountPath;

@MountPath("/index")
public class Index extends WebPage {
	private static final long serialVersionUID = 1L;

	public Index() {
		add(new Link("link"){
			public void onClick(){
				setResponsePage(new Another());
			}
		});
		add(new DropDownLocale("locale"));
	}
}

Index.html

<!DOCTYPE html>
<html xmlns:wicket="http://wicket.apache.org">
	<head>
		<meta charset="UTF-8">
		<title>Insert title here</title>
	</head>
	<body>
		<a href="#" wicket:id="link">
			<wicket:message key="test">Hello</wicket:message>
		</a>
		<select wicket:id="locale"></select>
	</body>
</html>

Index.properties

test=Hello World!

Index_es.properties

test=¡Hola mundo!
03
Ene
13

El principio… como siempre tarde

Voy a intentar no aburrir a nadie. La idea de todo esto es simplemente ir recopilando aquellas cosas de interés (por lo curioso, lo complejo, lo marciano) que durante los desarrollos me vayan surgiendo. Si de paso, le sirve de ayuda a alguien bienvenido sea.

Como recordatorio para mí mismo, me apunto aquí el enlace a la documentación de WordPress para el formateo de código.




Archivos