package de.flower.common.ui.ajax.markup.repeater;
import de.flower.common.model.db.entity.IIdentifiable;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.RefreshingView;
import org.apache.wicket.model.IModel;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Refreshing view that can be partially updated by ajax calls (appends new elements to end of list).
*
* In html:
*
* <pre>
* <ul wicket:id="container">
* <li wicket:id="list">
* <span wicket:id="name" />
* ....
* </li>
* </ul>
* </pre>
*
* In java:
*
* <pre>
* WebMarkupContainer container = new WebMarkupContainer("container");
* RefreshingView listView = new AjaxAppendingRefreshingView("list") {
* ....
* }
* </pre>
*
* Append new items to list as part of an ajax response
*
* <pre>
* AjaxLink link = new AjaxLink() {
* public void onClick(AjaxRequestTarget target) {
* listView.appendNewItems(target, getNewItems());
* }
* }
* </pre>
*
* Or let list update itself with ajax timer behavior. List will detect new items.
*
* <pre>
* container.add(new AbstractAjaxTimerBehavior(Duration.seconds(10)) {
* public void onTimer(AjaxRequestTarget target) {
* listView.updateItems(target);
* }
* }
*
* </pre>
*
* @see http://wicketinaction.com/2008/10/repainting-only-newly-created-repeater-items-via-ajax/
* @see http://donteattoomuch.blogspot.com/2008/04/partial-ajax-update-capable-list-view.html
*
* @param <T> generic type of list elements.
*/
public abstract class AjaxAppendingRefreshingView<T extends IIdentifiable<?>> extends RefreshingView<T> {
private int currentIndex;
private final List<Serializable> idList = new ArrayList<Serializable>();
/**
* Instantiates a new self appending refreshing view.
*
* @param id the id
* @param model the model
*/
public AjaxAppendingRefreshingView(final String id, final IModel<?> model) {
super(id, model);
}
@Override
protected void onInitialize() {
super.onInitialize();
getParent().setOutputMarkupId(true);
}
/**
* Iterates over new items and appends them to the existing list.
*
* @param target the target
* @param newItems the new items
*/
public final void appendNewItems(final AjaxRequestTarget target, final Iterator<IModel<T>> newItems) {
for (final Iterator<IModel<T>> iterator = newItems; iterator.hasNext(); ) {
IModel<T> model = iterator.next();
final String id = newChildId();
final Item<T> item = newItem(id, currentIndex + 1, model);
populateItem(item);
item.setOutputMarkupId(true);
add(item);
target.prependJavaScript(getJavaScriptToRenderNewDomElement(item));
target.add(item);
}
}
/**
* Update items. List will call {@link #getListItems()} and determine which items are new and add those new items to
* the list.
*
* @param target the target
*/
public final void updateItems(final AjaxRequestTarget target) {
final List<IModel<T>> newItems = new ArrayList<IModel<T>>();
for (final Iterator<IModel<T>> iterator = getItemModels(); iterator.hasNext(); ) {
final IModel<T> itemModel = iterator.next();
final T object = itemModel.getObject();
if (!idList.contains(object.getId())) {
newItems.add(itemModel);
}
}
if (!newItems.isEmpty()) {
appendNewItems(target, newItems.iterator());
}
}
private String getJavaScriptToRenderNewDomElement(final Item<T> item) {
// can use any element tag. does not have to match list type. will be replaced by wicket ajax-update.
final String script = String.format("var item=document.createElement('%s');item.id='%s';"
+ "Wicket.$('%s').appendChild(item);", "span", item.getMarkupId(), getParent().getMarkupId());
return script;
}
@Override
protected final Item<T> newItem(final String id, final int index, final IModel<T> model) {
// must track current index.
this.currentIndex = index;
// also track ids of rendered items to be able to detect new items when updating list.
idList.add(model.getObject().getId());
return super.newItem(id, index, model);
}
}