Since Wicket 1.1, it has been possible to do what we call 'header contributions'. You would use header contributions when you want a component to 'contribute' to the head section of the page it is placed in. For example, the date picker component needs JavaScript and CSS to function properly. Using header contributions, components can be truly self contained and keep their dependencies implementation details that users of components do not have to know about.
The basic pattern to achieve this in 1.1, was to let components implement wicket.markup.html.IHeaderContributor, and write the contributions directly to the response that is provided in that interface's method renderHead(wicket.Response repsonse). While that works, it's far from elegant and it doesn't provide you the flexibility adding header contributions through composition instead of via inheritance.
From Wicket 1.2 on, you can use wicket.behavior.HeaderContributors to let components do header contributions. Like other Wicket behaviors (e.g. wicket.behavior.AttributeModifiers), you can add these behaviors to components using the Component.add(IBehavior) method.
There are several convenience methods on HeaderContributor. E.g.
c.add(HeaderContributor.forJavaScript(MyComponent.class, "myscript.js");
adds the packaged JavaScript file that sits in the same package as MyComponent. But you can also do things like
HeaderContributor contributor = new HeaderContributor();
contributor.addContributor(new IHeaderContributor() {
public void renderHead(Response response) {
... write to response directly
}
});
Further convenience is provided by wicket.behavior.StringHeaderContributor, which contributes the literal string you provide it. That's generally a bit more elegant than just writing to the response directly.
Finally, we combined packaged resources, variable substitution and header contribution to make contributing dynamic JavaScript (or whatever dynamic header part you want) less cumbersome. For example, this is how the YUI (Yahoo User Interface) extensions project at wicket-stuff contributes it's JavaScript initialization script. The code for the model:
IModel variablesModel = new AbstractReadOnlyModel() {
/** cached variables; we only need to fill this once. */
private Map variables;
public Object getObject(Component component) {
if (variables == null) {
this.variables = new MiniMap(7);
variables.put("javaScriptId", javaScriptId);
variables.put("backGroundElementId", backgroundElementId);
variables.put("imageElementId", imageElementId);
variables.put("leftUp", settings.getLeftUp());
variables.put("rightDown", settings.getRightDown());
variables.put("tick", settings.getTick());
variables.put("formElementId", element.getId());
}
return variables;
}
};
The code to add a contributing behavior that contributes init.js from the package of Slider and that uses the model we created above for variable substitution (note that currently, TextTemplateHeaderContributor can be found in the wicket-extensions project):
add(TextTemplateHeaderContributor.forJavaScript(
Slider.class, "init.js", variablesModel));
And finally the contents of init.js:
var ${javaScriptId};
function init${javaScriptId}() {
${javaScriptId} = YAHOO.widget.Slider.getHorizSlider(
"${backGroundElementId}", "${imageElementId}",
${leftUp}, ${rightDown}, ${tick});
${javaScriptId}.onChange = function(offsetFromStart) {
document.getElementById("${formElementId}").value = offsetFromStart;
}
}
There are many ways you can combine these classes. Play around with it and find out!