Wicket header contributions with behaviors

3 05 2006

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!


Actions

Information

4 responses

10 11 2006
rue’s headroom » Blog Archiv » links for 2006-10-26

[...] Wicket header contributions with behaviors How to use text templates with variables for dynamic CSS or JavaScript in Wicket. (tags: wicket css javascript template) [...]

13 06 2008
freelancer

Wicket would not contribute to the header if the panel is not visible. A kind of “optimization” from wicket. This is not always good. Is there a way to force contribution to the header, no matter if panel is visible or not? thanks!

13 06 2008
Eelco Hillenius

Why would you need the header if no component that is actually using the header is rendered? What ‘isVisible’ does for components isn’t always clear to people; maybe a better name would have been ‘willBeRendered’ or something.

Anyway, if you think the header contribution should be done even if the component isn’t rendered, that probably means that you are doing the contribution in the wrong place and you should rethink how the contribution and component relate to each other. The likely conclusion is that you’ll have to ‘move up’ the contribution your component tree to ensure it is done regardless or not whether the component it was contributed by first is rendered.

14 08 2009
Rosario

woow… Very nice post, Thanks for share :)

Leave a comment