Wicket in Action

A comprehensive guide for Java developers building Wicket-based web applications

Repainting only newly-created repeater items via ajax

Ajax in Wicket is great, for the most part its usage is transparent and things work as you would expect. Where Ajax does not work perfectly, yet, is when it comes to partially updating repeaters. The problem is that repeater components do not have a markup tag of their own and so Wicket cannot transparently figure out where the updates should go.

Consider a simple case of a repeater rendering a list of contacts. Wicket markup looks like this:

1
2
3
4
5
6
7
<body>
  <table>
    <tr wicket:id="contacts">
      <td><span wicket:id="name"></span></td>
    </tr>
  </table>
</body>

And the rendered markup looks like this:

1
2
3
4
5
6
<body>
  <table>
    <tr><td><span>Lisa Simpson</span></td></tr>
    <tr><td><span>Bart Simpson</span></td></tr>
  </table>
</body>

As you can see, if we add a new item to the ListView and try to repaint it via Ajax there is no root markup tag for the Wicket Ajax to replace. This is why it is necessary to add the repeater to a container and then repaint the container instead. In this case we would add a WebMarkupContainer to the table tag and repaint it via Ajax instead. This, however, will result in all table rows being rendered and sent over from server to client, which for a lot of cases is undesirable.

It is not too hard to support the usecase of “I added something new to the repeater and want to have just that row added via Ajax”. The trick is to give Wicket a tag to repaint via Ajax which can be accomplished by doing the following:

  1. create the markup tag to represent the new item
  2. add it to the right place in the markup
  3. have Wicket repaint it via Ajax

Accomplishing this is pretty easy. Suppose we create the new item by submitting a form via an AjaxButton:

form.add(new AjaxButton("submit")
  {
    @Override
    protected void onSubmit(AjaxRequestTarget target, Form< ? > f)
    {
      // retrieve the newly added contact bean
      Contact contact = form.getModelObject();
      // add it to the collection on serverside
      contacts.add(contact);
      // create the new repeater item and add it to the repeater
      Component item = buildItem(contact);
      // first execute javascript which creates a placeholder tag in markup for this item
      target.prependJavascript(
        String.format(
        "var item=document.createElement('%s');item.id='%s';"+
        "Wicket.$('%s').appendChild(item);",
        "tr", item.getMarkupId(), container.getMarkupId()));
 
        // notice how we set the newly created item tag's id to that of the newly created
        // Wicket component, this is what will link this markup tag to Wicket component
        // during Ajax repaint
 
        // all thats left is to repaint the new item via Ajax
        target.addComponent(item);
      }
    });

When the form is submitted using the above AjaxButton only the newly added item is repainted via Ajax instead of the entire table. A functional project is attached below.

This concept can be taken to the next level where a repeater can be “synced” with the markup on the client side. This would involve:

  1. Building a list of item ids currently in the repeater
  2. Invoking #onBeforeRender on the repeater to make it generate new items
  3. Building a list of new item ids in the repeater
  4. Syncing the client markup from old items to new items by deleting removed items and adding new items via the same technique as above

Maybe next time :)

Sample project: partialajax.zip

12 Comments »

  1. Hi Igor. Yet another great tutorial, thanks. But how would you delete an item? Or add a row not at the end but somewhere in the middle? I’d guess that it works with plain JS.

    // remove item
    target.prependJavascript(
    String.format(
    “Wicket.$(‘%s’).removeChild(Wicket.$(‘%s’));”,
    “tr”,container.getMarkupId(),item.getMarkupId()));

    // add after existingItem
    target.prependJavascript(
    String.format(
    “var item=document.createElement(‘%s’);item.id=’%s’;”+
    “Wicket.$(‘%s’).insertBefore(item, Wicket.$(‘%s’));”,
    “tr”,item.getMarkupId(), container.getMarkupId(), oldItem.getMarkupId()));

    getting the old items from the repeater shouldn’t be a problem, right? I hope my still-tired brain doesn’t fool me here :)

    Comment by Stefan Fussenegger — October 24, 2008 @ 9:46 am

  2. @Stefan: That’s exactly right. Getting old items is easy, they are all direct children of the repeater. Obviously, it gets a little bit more complicated for a GridView because there you are looking for grid items that are not direct children, but that is true for any high-level repeater that embeds another repeater inside.

    Comment by ivaynberg — October 24, 2008 @ 5:46 pm

  3. Great example! Could this be done with a RefreshingView and an AjaxLink?

    Comment by Jeff — November 7, 2008 @ 11:54 pm

  4. @Jeff: It should be possible albeit a little harder. You will have to iterate over all existing items, see which ones have been removed and added and spit out javascript to synchronize server with client. Component#hasBeenRendered() can come in handy when figuring out which items have been left over from the previous request.

    Comment by ivaynberg — November 8, 2008 @ 12:30 am

  5. I found this link as helpfulon this. I think I see what i need..
    http://donteattoomuch.blogspot.com/2008/04/partial-ajax-update-capable-list-view.html

    Comment by glenn — November 16, 2008 @ 2:00 pm

  6. Nice example, note there is a bug I believe. onSubmit of form, you should create a new contact object and associate it with your form model. Otherwise, you will continue to re-use the same single contact instance that was supplied on form creation.

    I created an ajaxlink to remove that seems to work …

    add(new AjaxLink(“remove”)
    {
    public void onClick(AjaxRequestTarget target)
    {
    String markupId = …

    target.prependJavascript(“var item=document.getElementById(‘”
    + markupId + “‘);Wicket.$(‘” + container.getMarkupId()
    + “‘).removeChild(item);”);

    contacts.remove(…);
    repeater.remove(…);
    }
    });

    Comment by Matt Shannon — May 8, 2009 @ 8:33 am

  7. Very nice. But how to get markupId for the remove operation?

    Comment by sven — June 30, 2009 @ 3:13 pm

  8. Hi again, I’m having big problems with refresh.
    After adding the rows to dataView (refreshingView in your case), they are displayed correctly. But after F5 (refresh), they disappear.
    I added ‘expanded’ property to model item. And when populating the item (populateItem), I check it. If it is set, I create the new rows with your buildItem method. But as the result, the rows are displayed above the current row, which is logical, but wrong.
    How can the rows be displayed under the current row (with which I work in populateItem method), please?
    Thanks a lot

    Comment by sven — June 30, 2009 @ 4:15 pm

  9. Note – they cannot be added into the collection of contacts (which works nicely), because they act as subrows – they are little different then the main rows.

    Comment by sven — June 30, 2009 @ 4:17 pm

  10. For record – maybe I found solution – add them into collection and use fragments for the two different styles of rows.

    Comment by sven — June 30, 2009 @ 5:11 pm

  11. A nice tutorial, thanks. However if you want less Javascript magic and more Wicket, then I don’t think you should be doing such a trick to save a few hundred bytes of bandwidth. This blog entry provides a solution for this case:
    http://oktech.hu/blog/2009/07/adding-and-removing-rows-in-wicket.html

    Comment by monika — July 28, 2009 @ 11:45 am

  12. @monika: Based on your table it can be quiet a lot of data. Also based on complexity of the data – you might not want to run the server side code/queries necessary to rebuild that data. Anyways, the point of this article was to demonstrate a partial update to the table, not a partial update to the page which is trivial in Wicket.

    Comment by ivaynberg — July 30, 2009 @ 7:02 pm

RSS feed for comments on this post. TrackBack URL

Leave a comment

With this book, Wicket will become the greatest territory the Dutch have settled since Manhattan.

Nathan Hamblen
Senior Software Engineer, Teachscape Inc.

This is the complete and authoritative guide to Wicket, written and reviewed by the core members of the Apache Wicket team. If there's anything you want to know about Wicket, you are sure to find it in this book.

Jonathan Locke
Founder and Architect of Apache Wicket, Foreword Wicket in Action

Without question, Wicket in Action... is the be-all and end-all when it comes to Wicket.

Geertjan Wielenga, Wicket Netbeans Plugin Author

The tutorial and conversational tone of the writing makes the book very approachable.

Nick Heudecker
System Mobile

Loved the sample application—it tied everything together.

Phil Hanna
Senior Software Developer, SAS Institute

The essential guide for learning and using Wicket.

Erik van Oosten
Lead programmer and Project Manager, JTeam

Finally, the Web Framework of web frameworks, Apache Wicket, now has a bible of its own.

Per Ejeklint
Senior Software Architect, Heimore group

Wicket is an innovative evolution of the MVC programming with simple roots, but without a primer such as this, it can be more challenging than it needs to be.

Brian Topping
Founder, Bill2 Inc.

Wicket In Action glues the areas of web development with Apache Wicket together and gives a great overview of Apache Wicket...it will make a great compendium.

Nino Martinez Wael
Java Specialist, Jayway Denmark