Provides a specialized panel and some related utilities that enables users to work with Mustache and Apache Wicket.
wicket-mustache dependes on mustache.java.
Documentation:
- Mustache.js manual
- Mustache.java manual
- Passes all of the
mustache
specification tests modulo whitespace differences
Add maven dependency:
<dependency>
<groupId>de.agilecoders.wicket.mustache</groupId>
<artifactId>wicket-mustache</artifactId>
<version>0.1.0</version>
</dependency>
Installation:
/**
* @see org.apache.wicket.Application#init()
*/
@Override
public void init() {
super.init();
WicketMustache.install(this);
}
Example template file:
{{#items}}
Name: {{name}}
Price: {{price}}
{{#features}}
Feature: {{description}}
{{/features}}
{{/items}}
Might be powered by some backing code:
public class Context implements IScope {
List<Item> items() {
return Arrays.asList(
new Item("Item 1", "$19.99", Arrays.asList(new Feature("New!"), new Feature("Awesome!"))),
new Item("Item 2", "$29.99", Arrays.asList(new Feature("Old."), new Feature("Ugly.")))
);
}
static class Item {
Item(String name, String price, List<Feature> features) {
this.name = name;
this.price = price;
this.features = features;
}
String name, price;
List<Feature> features;
}
static class Feature {
Feature(String description) {
this.description = description;
}
String description;
}
}
And would result in:
Name: Item 1
Price: $19.99
Feature: New!
Feature: Awesome!
Name: Item 2
Price: $29.99
Feature: Old.
Feature: Ugly.
Evaluation of the template proceeds serially. For instance, if you have blocking code within one of your callbacks you the system will pause while executing them:
static class Feature {
Feature(String description) {
this.description = description;
}
String description() throws InterruptedException {
Thread.sleep(1000);
return description;
}
}
If you change description to return a Callable
instead it will automatically be executed in a separate
thread if you have provided an ExecutorService
when you created your MustacheFactory
.
Callable<String> description() throws InterruptedException {
return new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(1000);
return description;
}
};
}
This enables scheduled tasks, streaming behavior and asynchronous i/o. Check out the samples
module in order
to see a complete end-to-end example:
package de.agilecoders.wicket;
import de.agilecoders.wicket.mustache.IScope;
import de.agilecoders.wicket.mustache.MustachePanel;
import org.apache.wicket.core.util.resource.PackageResourceStream;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.util.resource.IResourceStream;
import java.util.Arrays;
import java.util.List;
public class HomePage extends WebPage {
private static final long serialVersionUID = 1L;
public HomePage(final PageParameters parameters) {
super(parameters);
IModel<IScope> scopeModel = new LoadableDetachableModel<IScope>() {
@Override
public IScope load() {
return new Example();
}
};
add(new MustachePanel("template", scopeModel) {
@Override
protected IResourceStream newTemplateResourceStream() {
return new PackageResourceStream(HomePage.class, "template.mustache");
}
});
}
public static class Example implements IScope {
public List<Item> items() {
return Arrays.asList(
new Item("Item 1", "$19.99", Arrays.asList(new Feature("New!"), new Feature("Awesome!"))),
new Item("Item 2", "$29.99", Arrays.asList(new Feature("Old."), new Feature("Ugly.")))
);
}
static class Item {
Item(String name, String price, List<Feature> features) {
this.name = name;
this.price = price;
this.features = features;
}
String name, price;
List<Feature> features;
}
static class Feature {
Feature(String description) {
this.description = description;
}
String description;
}
}
}
If you want to render your mustache template on client side you have two options: ClientSideMustachePanel
and LazyLoadingClientSideMustachePanel
.
add(new ClientSideMustachePanel("template-client", scopeModel) {
@Override
protected IResourceStream newTemplateResourceStream() {
return new PackageResourceStream(HomePage.class, "template.mustache");
}
});
add(new LazyLoadingClientSideMustachePanel("template-lazy", scopeModel) {
@Override
protected IResourceStream newTemplateResourceStream() {
return new PackageResourceStream(HomePage.class, "template.mustache");
}
@Override
protected CharSequence loading() {
return "please wait...";
}
@Override
protected Duration delay() {
return Duration.seconds(5);
}
});