We chose the Wicket framework several years ago because at the time it had the best-of-breed model for separating declarative content from code. Unfortunately the state model and complexity of the code make it more awkward than it should be to use. Nonethless, it makes an excellent, highly declarative and flexible web framework with StrataCode and there are several demos part of the StrataCode test suite that we continually run in both compiled and dynamic modes.
These days we are doing all future work in the StrataCode web framework.
StrataCode on Wicket
Wicket is a web application framework, providing component based development support for HTML and AJAX applications.
To start using wicket with all of the StrataCode features enabled, extend the wicket.core layer. Wicket supports most aspects of the dynamic model on page refresh. You must restart when you add or modify the path of a wicket application.
Using the same "extendedModel" layer from the previous unitConverter samples, add a wicketui layer:
package sc.example.unitConverter; example.unitConverter.wicketui extends wicket.core, extendedModel { }Each root URL is mapped to a wicket application component. StrataCode simplifies this process some by generating the web.xml from an annotation. You also must extend the wrapper class WicketApplication which has hooks necessary for delivering binding events:
@ApplicationPath("/*") public class UnitConverterApp extends WicketApplication { homePage = UnitConverter.class; }
Each Wicket Page is defined by a Java class. You manage navigation by setting the responsePage property of a currently executing page to either an existing Page instance or a new instance you create. Typically the page object has references to all state required to satisfy that page. All output is performed by wicket templates: one template for each Java component. The HTML file defines a wicket component hierarchy inside of the HTML tag hierarchy. Only wicket tags or tags with wicket:id are added to the wicket component tree. Each wicket id refers to an inner object in the Page class with that name in the HTML hierarchy.
For the UnitConverter application we have both a StrataCode file:
UnitConverter extends CWebPage { // Converts the value strings to/from numbers. numberToString to defines // a reverse method so it can be used in 2-way bindings. transient object numberConverter extends sc.util.NumberConverter { } // Wicket requires this of model objects Converter implements java.io.Serializable { } int currentConverterIndex = 0; Converter currentConverter = converters.get(currentConverterIndex); object feedback extends FeedbackPanel { outputMarkupId = true; } object unitConverterForm extends CForm<UnitConverter> { object converterChoice extends CDropDownChoice { choiceValues = converters; currentValue :=: currentConverter; selectionChangedNotifications = true; } object unit1Label extends CLabel { textValue := currentConverter.unit1; } object unit1Field extends CTextField<Double> { required = true; type = Double.class; fieldValue :=: currentConverter.value1; } object unit2Label extends CLabel { textValue := currentConverter.unit2; } object unit2Field extends CTextField<Double> { required = true; type = Double.class; fieldValue :=: currentConverter.value2; } } public UnitConverter() { } }and a parallel HTML file:
<html xmlns:wicket="http://wicket.apache.org/">
<head>
<title>UnitConverter on StrataCode</title>
<link rel="stylesheet" type="text/css" href="style.css"/>
</head>
<body>
<form wicket:id="unitConverterForm" id="unitConverterForm">
<select wicket:id="converterChoice">
<option>no converters</option>
</select>
<p>
<span wicket:id="unit1Label">unit1Label</span>
<input wicket:id="unit1Field" autocomplete="off"/>
<br>
<span wicket:id="unit2Label">unit2Label</span>
<input wicket:id="unit2Field" autocomplete="off"/>
<p>
<input type="submit" value="Submit" id="formsubmit"/>
</form>
<div wicket:id="feedback">[[ errorPanel ]]</div>
</body>
</html>
So the wicket:id="unitConverterForm" attribute corresponds to UnitConverter.unitConverterForm because its inside of the UnitConverter page. Because wicket:Id="unit1Label" is inside unitConverterForm, it corresponds to the StrataCode object: UnitConverter.unitConverterForm.unit1Label.
Wicket enforces that your .java files and .html files be side-by-side in the code tree. With layering, you can individually override the Java and HTML definitions giving more flexibility for customizing wicket apps.
To make an Ajax version of wicket UnitConverter, there are only a few differences to the code because of the nice, declarative power of wicket. You turn on the outputMarkupId property so client-side HTML tags preserve the wicket:id attribute. You set the changeEvent property for the text field component to some client-side event like "onkeyup". You also set the throttleDelay property to indicate how long after the user stops typing before the event is sent.
Ordinarily in wicket, for the ajax mode to work you also need to explicitly mark any changed components. The HTML for those components is sent back to the client and they are patched into the DOM providing incremental refresh. The cost is more tricky code. With StrataCode's data binding, any components whose model properties change during the request are automatically marked as changed. This small change removes a big source of errors and makes ajax development virtually the same code as non-ajax for applications which can be built with data binding.
See example/unitConverter/wicketAjax/UnitConverter.sc for the working ajax example.