Skip to content


Quelltext der Beispiele aus dem Buch zum Download

Ich habe gerade die Beispiele aus dem Buch zum einem Paket geschnürt. Dieses kann man sich auf der Download-Seite herunterladen und ausprobieren. Alles weitere steht auf der Seite.

Das Quelltextarchiv wird in den nächsten Tagen auch auf der Verlagsseite zum Buch Praxisbuch Wicket zu finden sein.

Tags:

Veröffentlicht in Allgemein, Wicket, .

Download

Hier finden Sie verschiedene Resourcen rund um Wicket zum Download

Update

Sie finden alle aktualisierten Quelltexte zum Buch und alle Beispiel-Codes bei GitHub unter folgender Url: https://github.com/michaelmosmann/wicket-praxis

Codebeispiele

Hier können Sie die Codebeispiele aus dem Buch "Praxisbuch Wicket" herunterladen: Praxisbuch Wicket Beispielcode

Entpacken Sie das Archiv in ein Verzeichnis ihrer Wahl. Installieren Sie Java und Maven. Wechseln Sie in das Verzeichnis de.wicketpraxis--parentPom und öffnen Sie eine Konsole. Rufen Sie in dem Verzeichnis dann mvn install auf. Nachdem der Vorgang angeschlossen wurde wechseln Sie in das Verzeichnis de.wicketpraxis--webapp und rufen entweder mvn jetty:run auf oder besser starten jetty.sh unter Linux oder jetty.bat unter windows.

Wenn Sie mit einem Browser dann die Adresse http://localhost:8080/ aufrufen, klicken Sie auf den ersten Link um zur Webanwendung zu gelangen. Danach sehen Sie bereits eine einfache Navigationsstruktur, über die Sie alle Beispiel ansteuern und ausprobieren können.

Viel Spaß damit.

Eclipse Templates

Die Templates für die Wicketunterstützung können Sie wie folgt importieren: Menü "Window > Preferences", in dem Dialog unter "Web > HTML Files > Editor > Templates" den Button "Import" anklicken und die Wicket-Template-Datei importieren.

Updates

Neue Version

Ich habe bereits die ersten Rückmeldungen bekommen und werde demnächst eine neue Version zur Verfügung stellen. Bis dahin etwas Geduld.

Neue Version

Die neue Version steht jetzt zur Verfügung. Es wurde die verwendete Hibernate-Version angepasst und die Einstellungen für die Testdatenbank beim starten der Anwendung verwendet, so dass jetzt keine MySql-Datenbank mehr eingerichtet werden muss, um die Beispiele zu starten. Die Anpassungen sind im Persistenz-Teilprojekt in der Datei ReadMe.txt dokumentiert.

Eclipse Template

Eclipse Template-Datei auf der Seite hinzugefügt.

Datenbankeinträge auswählen mit der CheckGroup

Ich wurde gefragt, ob in meinem Buch (Praxisbuch Wicket) die Verwendung der CheckGroup-Komponente erläutert wird. Die Anwort ist einfach: ja.

Was in dem Beispiel aber nicht gezeigt wird, ist das dynamische Erzeugen der Auswahlliste durch Daten aus einer Datenbank. Der Absender hat sich aber genau für diesen Fall interessiert, so dass ich an dieser Stelle mal ein einfaches Beispiel geben möchte, wie man soetwas realisieren kann.

Das Beispiel

package de.wicketpraxis.web.blog.pages.questions.checkgroup.fromdb;

import java.util.ArrayList;
import java.util.List;

import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Check;
import org.apache.wicket.markup.html.form.CheckGroup;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.util.CollectionModel;
import org.apache.wicket.spring.injection.annot.SpringBean;

import de.wicketpraxis.persistence.beans.User;
import de.wicketpraxis.persistence.dao.UserDao;

public class CheckGroupFromDbPage extends WebPage
{
  @SpringBean
  UserDao _userDao;
  
  public CheckGroupFromDbPage()
  {
    LoadableDetachableModel<List<User>> userListModel=new LoadableDetachableModel<List<User>>()
    {
      @Override
      protected List<User> load()
      {
        return _userDao.findAll(0, 10);
      }
    };
    
    final IModel<Collection<Integer>> selectedModel = new CollectionModel<Integer>(new ArrayList<Integer>());
    
    add(new FeedbackPanel("feedback"));
    
    Form<Void> form=new Form<Void>("form")
    {
      @Override
      protected void onSubmit()
      {
        info("Elemente selectiert: "+selectedModel.getObject());
      }
    };
    
    CheckGroup<Integer> checkGroup=new CheckGroup<Integer>("checkGroup",selectedModel);
    form.add(checkGroup);
    
    checkGroup.add(new ListView<User>("list",userListModel)
    {
      @Override
      protected void populateItem(ListItem<User> item)
      {
        item.add(new Check<Integer>("check",Model.of(item.getModelObject().getId())));
        item.add(new Label("name",item.getModelObject().getName()));
      }
    });
    
    add(form);
    
  }
}

Und das Markup-File:

<html>
  <head>
    <title>CheckGroup from DB</title>
  </head>
  <body>
    <div wicket:id="feedback"></div>
    
    <form wicket:id="form">
      <wicket:container wicket:id="checkGroup">
      <wicket:container wicket:id="list">
        <input wicket:id="check" type="checkbox"> <span wicket:id="name"></span><br>
      </wicket:container>
      </wicket:container>
      <input type="submit" value="Auswählen">
    </form>
  </body>
</html>

Die Variable _userDao beinhaltet die Datenbankzugriffsklasse. Da wir die Variable als Field definiert haben, können wir ohne weiteres innerhalb der Klasse darauf zugreifen. Wicket sorgt für alle über die SpringBean-Annotation initialisierten Felder dafür, dass es mit der Serialisierung der Komponente keine Probleme gibt.

Als nächstes erstellen wir ein Modell, dass die gewünschte Auswahlmenge ermittelt. An dieser Stelle könnte man über ein CascadingLoadableDetachableModel (kein Wicket-Standard, siehe Buch Seite 65 oder hier im Blog) auch auf die Modelldaten eines Eingabefeldes zurückgreifen um die Abfrage einsprechende zu beeinflussen zu können. Das Ergebis ist eine Liste von Objekten.

Als zweiter erstellen wir ein Modell, in dem die IDs der ausgewählten Elemente landen. Um den Aufwand einer eigenen Implementierung einer Modellklasse für eine Collection von persistenten Objekten zu sparen, legen wir nur die ID des Elements, nicht das Element selbst in der Auswahlliste ab.

Das Feedbackpanel dient zur Anzeige die Message, die in der onSubmit-Methode des Formulars ausgegeben wird. Das Formular hat in diesem Fall kein eigenes Modell (nur der Einfachheit halber wird in dem Beispiel auf die Verwendung eines CompoundPropertyModel verzichtet).

Jetzt kommt der spannende Teil. Der CheckGroup-Komponente wird als Parameter das Modell übergeben, in der die Ausgewählten Elemente gesammelt werden sollen. Dann wird in der ListView für alle zur Verfügung stehenden Elemente eine Check-Komponente hinzugefügt, die als Modell nicht das Objekt, sondern die ID des Datenbankeintrags übergeben bekommt.

Funktionsweise

Wenn das Formular abgeschickt wird, ermittelt Wicket alle Check-Komponenten, die aktiviert waren. Für diese Komponenten wird der Inhalt des Modells ermittelt und der Auswahlliste hinzugefügt. Abschließend wird das Modell, dass die Auswahlliste beinhaltet entsprechend gesetzt. Dabei können beliebige Typen für die Auswahlliste benutzt werden, solange sie serialisierbar sind. Kommen die Daten aus einer Datenbank empfiehlt sich der Einsatz eines Primärschlüssels, anhand dessen das Objekt ermittelbar bleibt.

Tags:

Veröffentlicht in Allgemein, Wicket, .

Scala vs Java

Ich habe mir gestern ein Scala-Codebeispiel angesehen, dass Scala-Objekte von und nach JSON konvertieren kann. Ich muss zugeben, dass es ein wenig gedauert hat, bis ich den Code verstanden habe. Das liegt unter anderem daran, dass ich in Scala einfach noch nicht gut genug bin. Der andere wichtigere Punkt ist aber folgender:

Wenn man bisher mit einer Reihe von Java-Klassen ein Framework für eine bestimmte Aufgabe entwickelt hat, kann man die selbe Anforderung mit einigen wenigen Scala-Klassen abbilden.

Das bedeutet, dass sich die Funktionalität mit sehr viel weniger Code darstellen lässt. Das spiegelt die Befürchtung wieder, dass man mit Scala Code schreiben kann, der schwer zu vestehen ist. Doch dieser erste Anschein trügt. Der Code ist nur um vielfaches dichter und deshalb natürlich schwerer zu verstehen als ähnlich umfangreicher Java-Code. Der funktionsäquivalente Java-Code wäre aber entsprechend umfangreicher und kann im Zweifel schon deshalb nicht ansatzweise so gut überblickt werden. Ich kann es daher nicht nachvollziehen, dass dieser Punkt gegen Scala sprechen soll.

Ich glaube, dass die Entwicklung bei Scala insofern noch am Anfang steht, als dass das Ausloten der Sprachemöglichkeiten und die kreativen Auslegungen der Plattform noch in den Kinderschuhen steckt, gerade wenn man betrachtet, wo wir uns da im Java-Umfeld befinden. Doch diese Kinderschuhe sind bereits jetzt viel größer und der kleine Bub schaut entspannt über die Köpfe seiner Eltern hinweg.

Tags:

Veröffentlicht in Allgemein, scala, Technologie, .

Wicket Praxis - das Buch ist da

Ich habe heute mein Vorabexemplar von "Praxisbuch Wicket" erhalten. Toll. Es ist schon ein schönes Gefühl, das erste eigene Buch in den Händen zu halten. Das bedeutet auch, dass es jetzt mit Sicherheit pünktlich erscheinen wird (1.09.).

Danke an alle, die direkt und indirekt daran beteiligt waren.

Veröffentlicht in Allgemein, Wicket, .

Web 1.0 mit Wicket - Seitenparameter und Links

Mit Wicket ist es sehr einfach, Webanwendungen zu schreiben. Wenn man dem Wicket-Pfad folgt und keine besonderen Wünsche hat. Im folgenden besteht der Wunsch darin, den Zustand einer Seite in Seitenparametern abzulegen. Wenn durch einen Link, also eine Aktion, die der Nutzer wählen kann, nur ein Parameter verändert wird, sollen natürlich alle anderen Parameter unverändert weitergereicht werden.

Zuerst erstellen wir uns ein paar Hilfsklassen, welche die Handhabung wesentlich vereinfachen werden. Die Idee ist dabei folgende: die Parameter, die mit dem Request übergeben werden, werden auf Attribute einer JavaBean gemappt und mit Wicket-Bordmitteln in den entsprechenden Datentyp konvertiert. Aus der JavaBean kann man dann die gewünschten Werte auslesen. Die Attribute der JavaBean können dann neu gesetzt werden. Ein weiteres Mal wird mit Wicket-Bordmitteln aus den Attributen der JavaBean eine Liste von Parametern gewonnen, die dann (fast) direkt in einem Link benutzt werden können.

Zuerst erstellen wir uns eine Annotation, mit der wir die Attribute markieren, die von und in PageParameter umgewandelt werden sollen.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PublicProperty
{	
}

Als nächstes erstellen wir uns ein Interface, was von der JavaBean implementiert werden muss. Die Aufgabe des Interface liegt darin, sicherzustellen, dass von der JavaBean eine Kopie angefertigt werden kann, damit die Änderungen an der Kopie und nicht am Original vorgenommen werden.

import org.apache.wicket.IClusterable;

public interface PageStateBeanInterface<T extends PageStateBeanInterface<?>> extends IClusterable
{
	public T getClone();
}

Jetzt kommt der aufwendigste Teil. Wir schreiben uns eine Hilfsklasse, welche die Transformation von und in PageParameter durchführt.

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.apache.wicket.Application;
import org.apache.wicket.IConverterLocator;
import org.apache.wicket.PageParameters;
import org.apache.wicket.Session;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.util.convert.IConverter;

public class BeanPagePropertyUtil
{
	public static <B> PageParameters getBeanPageParameters(B bean)
	{
		return new PageParameters(getParameter(bean));
	}
	
	public static <B> PageParameters getBeanPageParameters(B bean,B defaults)
	{
		Map<String, Object> beanParameter = getParameter(bean);
		Map<String, Object> defaultParameter = getParameter(defaults);
		for (String s : defaultParameter.keySet())
		{
			Object defaultValue = defaultParameter.get(s);
			if (defaultValue!=null)
			{
				Object curValue = beanParameter.get(s);
				if (defaultValue.equals(curValue))
				{
					beanParameter.remove(s);
				}
			}
		}
		return new PageParameters(beanParameter);
	}
	
	protected static <B> List<String> getPublicProperties(B bean)
	{
		List<String> ret=new ArrayList<String>();
		
		Method[] methods = bean.getClass().getMethods();
		for (Method m : methods)
		{
			PublicProperty annotation = m.getAnnotation(PublicProperty.class);
			if (annotation!=null)
			{
				String name = m.getName();
				if (name.startsWith("get")) ret.add(name.substring(3));
				else
				{
					if (name.startsWith("is")) ret.add(name.substring(2));
				}
			}
		}
		
		return ret;
	}
	
	public static <B> Map<String,Object> getParameter(B bean)
	{
		Map<String,Object> ret=new HashMap<String, Object>();
		
		Locale locale = Session.get().getLocale();
		IConverterLocator converterLocator = Application.get().getConverterLocator();
		
		for (String s : getPublicProperties(bean))
		{
			PropertyModel<?> propertyModel = new PropertyModel(bean,s);
			IConverter converter = converterLocator.getConverter(propertyModel.getObjectClass());
			Object value = propertyModel.getObject();
			if (value!=null)
			{
				ret.put(s, converter.convertToString(value, locale));
			}
		}
		return ret;
	}

	public static <B> void setParameter(B bean,PageParameters pageParameters)
	{
		Locale locale = Session.get().getLocale();
		IConverterLocator converterLocator = Application.get().getConverterLocator();
		
		for (String s : getPublicProperties(bean))
		{
			PropertyModel<Object> propertyModel = new PropertyModel<Object>(bean,s);
			IConverter converter = converterLocator.getConverter(propertyModel.getObjectClass());
			String svalue = pageParameters.getString(s);
			if (svalue!=null)
			{
				propertyModel.setObject(converter.convertToObject(svalue, locale));
			}
			else
			{
				propertyModel.setObject(null);
			}
		}
	}

	public static <B> void setParameter(B bean,Map<String,?> parameter)
	{
		for (String s : getPublicProperties(bean))
		{
			if (parameter.containsKey(s))
			{
				PropertyModel<Object> propertyModel = new PropertyModel<Object>(bean,s);
				Object value=parameter.get(s);
				propertyModel.setObject(value);
			}
		}
	}
}

Es ist wichtig, darauf hinzuweisen, dass es vorgesehen ist, eine mit Standardwerten initialisierte JavaBean als Abgleich zu benutzen. So kann sichergestellt werden, dass Seitenparameter dann aus der Url entfernt werden, wenn der Wert dem Standardwert entspricht. Das reduziert die Gefahr für Double-Content-Probleme wesentlich.

Jetzt haben wir alles zusammen, um Seitenparameter in JavaBean-Attribute zu überführen und zurück wandeln zu können. Als nächstes benötigen wir noch ein paar Komponenten, die den Prozess der Parameterlistenerstellung und der Konvertierung für uns übernehmen. Dazu fügen wir der Seite eine Komponente hinzu, die folgendes Interface implementiert:

public interface PageStateInterface<B extends PageStateBeanInterface<B>>
{
	public B getState();
	public B getDefaults();
}

Die Komponente konvertiert die Seitenparameter in die Attribute und stellt über die Schnittstelle die beiden Zustände zur Verfügung. Damit andere Komponenten auf diese Werte zugreifen können, erstellen wir gleichzeitig eine Funktion, welche die Komponente im Komponentenbaum sucht.

import org.apache.wicket.Component;
import org.apache.wicket.Page;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.panel.Panel;

public class PageContext<B extends PageStateBeanInterface<B>> extends Panel implements PageStateInterface<B>
{
	private static final Logger _logger=LoggerFactory.getLogger(PageContext.class);

	B _defaults;

	B _state;

	public PageContext(String id, PageParameters pageParameters, B defaults)
	{
		super(id);

		_defaults=defaults;
		_state=_defaults.getClone();
		BeanPagePropertyUtil.setParameter(_state, pageParameters);
	}

	public B getDefaults()
	{
		return _defaults;
	}

	public B getState()
	{
		return _state.getClone();
	}

	public static <B extends PageStateBeanInterface<B>> PageStateInterface<B> getPageState(Page page, Class<? extends B> type)
	{
		NodeVisitor visitor=new NodeVisitor(type);
		page.visitChildren(PageContext.class, visitor);
		return visitor.getPageState();
	}

	static class NodeVisitor<B extends PageStateBeanInterface<B>> implements IVisitor<Component>
	{
		Class<B> _type;

		PageStateInterface<B> _pageState;

		public NodeVisitor(Class<B> type)
		{
			_type=type;
		}

		public PageStateInterface<B> getPageState()
		{
			return _pageState;
		}

		public Object component(Component component)
		{
			if (component instanceof PageContext)
			{
				PageContext rawContext=(PageContext) component;
				if (_type.isAssignableFrom(rawContext.getDefaults().getClass()))
				{
					_pageState=rawContext;
					return IVisitor.STOP_TRAVERSAL;
				}
			}
			return IVisitor.CONTINUE_TRAVERSAL;
		}
	}
}

Des weiteren erstellen wir eine Linkklasse, die sich um das setzen der richtigen Seitenparameter kümmert.

import java.util.Map;

import org.apache.wicket.Page;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.util.collections.MiniMap;

public class PageStateLink<P extends Page, B extends PageStateBeanInterface<B>> extends BookmarkablePageLink<P>
{
	Map<String, ?> _linkParameter;

	Class<B> _beanType;

	public PageStateLink(String id, Class<P> pageClass, Class<B> beanType, Map<String, ?> linkParameter)
	{
		super(id, pageClass);
		_beanType=beanType;
		_linkParameter=linkParameter;
		if (_linkParameter == null) _linkParameter=new HashMap<String,?>();
	}

	public PageStateLink(String id, Class<P> pageClass, Class<B> beanType)
	{
		this(id, pageClass, beanType, null);
	}

	@Override
	protected void onBeforeRender()
	{
		PageStateInterface<B> pageState=PageContext.getPageState(getPage(), _beanType);
		if (pageState != null)
		{
			B bean=pageState.getState();
			B defaults=pageState.getDefaults();
			BeanPagePropertyUtil.setParameter(bean, _linkParameter);
			onAfterSetParameter(bean);
			PageParameters beanPageParameters=BeanPagePropertyUtil.getBeanPageParameters(bean, defaults);
			applyPageParameter(beanPageParameters);
		}
		super.onBeforeRender();
	}

	protected void onAfterSetParameter(B bean)
	{

	}

	private void applyPageParameter(PageParameters pageParameters)
	{
		this.parameters=pageParametersToMiniMap(pageParameters);
	}

	private MiniMap<String, Object> pageParametersToMiniMap(PageParameters parameters)
	{
		if (parameters != null)
		{
			MiniMap<String, Object> map=new MiniMap<String, Object>(parameters, parameters.keySet().size());
			return map;
		}
		else
		{
			return null;
		}

	}
}

Die Methode applyPageParameter wurde nur aus Geschwindigkeitsgründen erstellt, da sonst jeder Aufruf von setParameter dazu führt, dass die interne Map kopiert wird.

Um auf die JavaBean über ein Modell zuzugreifen, schreiben wir uns noch eine Modellklasse, bevor wir uns dann ansehen, wie man die Klassen dann benutzt.

import org.apache.wicket.Component;
import org.apache.wicket.model.LoadableDetachableModel;

public class PageStateModel<B extends PageStateBeanInterface<B>> extends LoadableDetachableModel<B>
{
	Component _component;
	Class<? extends B> _type;
	
	public PageStateModel(Component component, Class<? extends B> type)
  {
		_component=component;
		_type=type;
  }
	
	@Override
	protected B load()
	{
	  PageStateInterface<B> pageState=PageContext.getPageState(_component.getPage(), _type);
	  if (pageState!=null)
	  {
	  	return pageState.getState();
	  }
		return null;
	}
}

Ok. Das war ganz schön aufwendig, aber dafür ist die Verwendung um so einfacher. Wir erstellen eine JavaBean und eine Seite, auf der wir dann die Komponenten einbinden.

public class ConfigBean implements PageStateBeanInterface<ConfigBean>
{
	Integer _start;
	Integer _stop;
	String _name;

	@PublicProperty
	public Integer getStart()
	{
		return _start;
	}

	public void setStart(Integer start)
	{
		_start=start;
	}

	@PublicProperty
	public Integer getStop()
	{
		return _stop;
	}

	public void setStop(Integer stop)
	{
		_stop=stop;
	}

	@PublicProperty
	public String getName()
	{
		return _name;
	}

	public void setName(String name)
	{
		_name=name;
	}

	public ConfigBean getClone()
	{
		ConfigBean ret=new ConfigBean();
		ret._name=_name;
		ret._start=_start;
		ret._stop=_stop;
		return ret;
	}
}
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.PropertyModel;

public class TestPage extends WebPage
{
	IModel<ConfigBean> _config=new PageStateModel<ConfigBean>(this, ConfigBean.class);

	public StatelessTestPage(PageParameters pageParameters)
	{
		add(new PageContext<ConfigBean>("context", pageParameters, new ConfigBean()));

		add(new Label("start", new PropertyModel<Integer>(_config, "start")));
		add(new Label("stop", new PropertyModel<Integer>(_config, "stop")));
		add(new SubPanel("sub"));
	}

	public static class SubPanel extends Panel
	{
		IModel<ConfigBean> _config=new PageStateModel<ConfigBean>(this, ConfigBean.class);

		public SubPanel(String id)
		{
			super(id);

			add(new Label("name", new PropertyModel<Integer>(_config, "name")));

			PageStateLink<StatelessTestPage, ConfigBean> link=new PageStateLink<StatelessTestPage, ConfigBean>("link", StatelessTestPage.class, ConfigBean.class, new HashMap<String,Object>("Name","Klaus"));
			add(link);
			PageStateLink<StatelessTestPage, ConfigBean> link2=new PageStateLink<StatelessTestPage, ConfigBean>("link2", StatelessTestPage.class, ConfigBean.class, new HashMap<String,Object>("Start", 1));
			add(link2);
			PageStateLink<StatelessTestPage, ConfigBean> link3=new PageStateLink<StatelessTestPage, ConfigBean>("link3", StatelessTestPage.class, ConfigBean.class, new HashMap<String,Object>("Start", null));
			add(link3);
			PageStateLink<StatelessTestPage, ConfigBean> link4=new PageStateLink<StatelessTestPage, ConfigBean>("link4", StatelessTestPage.class, ConfigBean.class, new HashMap<String,Object>("Name", "Bert", "Stop", null));
			add(link4);
		}
	}
}

Wie man sieht, muss ich bei den Modellen eigentlich nichts besonderes machen. Die Links übergibt man eine Map mit neuen Parametern, mit denen der aktuelle Zustand, der in der JavaBean gespeichert wurde, für diesen Link, diese Nutzeraktion überschrieben wird. Dabei spielt es keine Rolle, in welcher Komponente so ein Link benutzt wird, da sich die Linkklasse und die Modellklasse selbsttätig um die Informationen bemühen.

Der hier vorgeschlagene Ansatz ist sicher a) verbesserungswürdig und b) ausbaufähig. Er soll als Anregung dienen, wie man dieses und möglicherweise ähnliche Probleme lösen kann und dabei besonders von der Komponentenarchitektur von Wicket profitieren kann.

Tags:

Veröffentlicht in Allgemein, Wicket, .

Wicket "stateless" - keine einfache Herausforderung

Wie in den Kommentaren der letzten Tage sichtbar wurde, eignet sich Wicket nicht "ungeschminkt" für Webseiten, die ihre Zustandsinformationen klassisch in URL-Parametern ablegen. In meinem Buch beschreibe ich eine einfache Variante, die bereits zum Ziel führen kann.

Die Herausforderung liegt darin, dass man von den Vorteilen, die Wicket mitbringt nicht all zu viele einbüßt. Daher werde ich mich in den nächsten Tagen mal damit beschäftigen, wie man mit möglichst wenig Aufwand Anwendungen realisieren kann, die auf der einen Seite URL-Parameter benutzen, auf der anderen Seite aber nicht auf saubere Komponenten verzichten müssen.

Auch wenn es schwer ist, sich von der Herangehensweise wie man sie mit JSP,PHP,... gewöhnt ist, zu lösen, erschließt man das volle Potential von Wicket erst dann, wenn man sich auf den "Wicket-Pfad" begibt. Der einzige Aspekt, der es notwendig macht, sich mit der Frage von URL-Parametern zu beschäftigen, ist die Suchmaschinentauglichkeit der Anwendung. Da muss man allerdings auch überlegen, ob man nicht einen Sitemap generiert, die alle gewünschten Inhalte ansteuerbar macht, so dass man nicht gezwungen ist, eine SEO-taugliche Navigation aufzusetzen.

Wicket ist ganz sicher nicht für jede Anforderung geeignet. Unbestritten. Die Alternativen sind IMHO allerdings auch nicht zufriedenstellender.

Tags:

Veröffentlicht in Allgemein, Refactoring, Wicket, .

Wicket saved the day

Wer schon Webanwendungen entwickelt hat, der wird der sehr gut geschriebenen Ausführung von Jeff Schwartz folgen können. Rückblickend ist es eigentlich erschreckend, wie naheliegend die Lösung die ganze Zeit war. Denn Wicket ist kein Hexenwerk, sondern einfach nur gut durchdachter Java-Code.

Tags:

Veröffentlicht in Wicket, .

Sails - die Scala Version von Rails/Grails

Ich habe mir das Scala Webframework Lift angesehen und finde, es ist zu kompliziert. Aber ich habe mir heute morgen auch wieder ein paar Möglichkeiten angesehen, die Scala so bietet und frage mich, ob man nicht (mit etwas Hilfe) ein Stack erstellen könnte, der Scala als Basissprache benutzt und sonst sich aber eher von Rails/Grails inspirieren lassen könnte. Für das UI käme natürlich Wicket in Frage.

Ich glaube, das wäre reizvoll, spannend und wichtig: sehr produktiv.

p.s.: wenn es sowas schon gibt, bitte gerne einen Hinweis.

Tags:

Veröffentlicht in scala, Technologie, Wicket, .

Grails Ajax Update zu kompliziert...

Man lernt ja jeden Tag etwas neues. Gestern habe ich gelernt, dass Grails von Haus aus nur ein Element per Ajax austauschen kann. Man klickt auf einen Link und kann dann z.B. den Inhalt eines DIV-Tags ersetzten, ergänzen oder was auch immer. Das ist alles ganz nett und einfach. Ändert sich aber durch eine Interaktion mehr als ein Element auf der Seite, fängt es an kompliziert zu werden. Wobei kompliziert eigentlich nicht das richtige Wort ist, denn man muss diese fehlende Funktionalität selbst ergänzen.

Es ist unzweifelhaft möglich, so das gewünschte Ziel zu erreichen. Aber ich finde es spannend, dass es eine IMHO derart triviale Anforderung nicht in den "Grails-Standard" geschafft hat. In Wicket füge ich die Komponenten, die aktualisiert werden müssen, einfach zu einer Liste hinzu. Ich muss mich nicht darum kümmern, was dann genau passiert.

Festzuhalten bleiben für mich die Punkte, die für mich eher gegen einen Einsatz von Grails sprechen:

  • Ajax Update nur für ein Element möglich.
  • Spring Security ist pfadbasiert - das ist IMHO ein extrem limitierter Ansatz.
  • Taglibs sind kein Ersatz für Komponenten.

Grails hat Stärken.. aber ich bezweifle zunehmend, dass in Projekten, die mit Grails angefangen wurden, Grails diese positiven Effekte aus dem Projektstart auch auf lange Sicht ausspielen kann.

Update:

Immerhin habe ich gerade wieder gelernt, dass man Spring Security auch mit Annotationen am Controller benutzen kann, so dass man die Pfade nicht von Hand aktualisieren muss, wenn man etwas umbaut.

Das ich hier vielleicht Wicket und Grails vergleiche, obwohl die beiden Technologien nicht so einfach vergleichbar sind, bezieht sich in dieser Kritik nur auf den View-Layer. Es gibt sehr viele Dinge, die ich recht cool finde, an Grails. Aber um View-Layer bin ich einfach besseres gewohnt.

Tags:

Veröffentlicht in Technologie, Wicket, .