All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.premiumminds.webapp.wicket.repeaters.AjaxListSetView Maven / Gradle / Ivy

/**
 * Copyright (C) 2016 Premium Minds.
 *
 * This file is part of pm-wicket-utils.
 *
 * pm-wicket-utils is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * pm-wicket-utils is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with pm-wicket-utils. If not, see .
 */
package com.premiumminds.webapp.wicket.repeaters;

import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections4.IteratorUtils;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.set.ListOrderedSet;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.AjaxRequestTarget.IJavaScriptResponse;
import org.apache.wicket.ajax.AjaxRequestTarget.IListener;
import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;

/**
 * 
 * An AjaxListSetView is a repeater that makes it easy to display/work with ListOrderedSets (collection with unique values, but with List order). 
 * This repeater is great when you have FormComponents for each row and want to change the list through AJAX, because only new elements of the list are
 * created in the DOM, the rest is just reordered.
 * 

* Example: * *

 * <table>
 *   <tr wicket:id="rows" class="even">
 *     <td><span wicket:id="id">Test ID</span></td>
 *     ...
 * 
* *

* The related Java code: * *

 * add(new AjaxListSetView<UserDetails>("rows", listModel, "tbody")
 * {
 * 		public void populateItem(final ListSetItem<UserDetails> item)
 * 		{
 * 			item.add(new Label("id", new PropertyModel(item.getModel, "id")));
 * 		}
 * });
 * 
* *

* NOTE: AjaxListSetView creates a container between his items and the markups parent. In the example above, AjaxListSetView will * insert the element tbody between table and tr. * *

* * @author acamilo * * @param type of elements contained in the model's list */ public abstract class AjaxListSetView extends AbstractRepeater2 { private static final long serialVersionUID = -2741997575388196264L; /** counter to generate rows index */ private int counter = 1; /** cache for the items already created */ private Map> elementToComponent = new HashMap>(); /** markup tag used to create new elements through ajax */ private String markupTag = null; /** * @param id component id * @param model model containing the listOrderedSet of * @param bodyTag tag name for the container that will be created * @see org.apache.wicket.Component#Component(String, IModel) */ public AjaxListSetView(String id, IModel> model, final String bodyTag) { super(id, model); if (model == null){ throw new IllegalArgumentException("Null models are not allowed"); } add(new Behavior() { private static final long serialVersionUID = -2448715969183335401L; @Override public void beforeRender(Component component) { getResponse().write("<"+bodyTag+" id='"+component.getMarkupId(true)+"'>"); } @Override public void afterRender(Component component) { getResponse().write(""); } }); } @Override protected void onInitialize() { super.onInitialize(); markupTag = ((ComponentTag) getMarkup().get(0)).getName(); } @Override protected Iterator renderIterator() { return IteratorUtils.transformedIterator(getList().iterator(), new Transformer>() { @Override public ListSetItem transform(T el) { ListSetItem component = elementToComponent.get(el); if(component==null) throw new IllegalStateException("could not find element '"+el+"' on computed map"); return component; } }); } private void updateItems(Set> newItems, Set> removedItems){ Set componentsNotHandled = new HashSet(elementToComponent.keySet()); for(T el : getList()){ if(componentsNotHandled.contains(el)){ componentsNotHandled.remove(el); } else { ListSetItem item = newItem(Model.of(el)); elementToComponent.put(el, item); newItems.add(item); add(item); } } for(T el : componentsNotHandled){ removedItems.add(elementToComponent.get(el)); remove(elementToComponent.get(el)); elementToComponent.remove(el); } } @Override protected void onPopulate() { Set> newItems = new HashSet>(); Set> removedItems = new HashSet>(); updateItems(newItems, removedItems); } /** * Create a new ListSetItem for list item. * * @param itemModel object in the list that the item represents * @return ListSetItem */ protected ListSetItem newItem(IModel itemModel) { ListSetItem item = new ListSetItem(Integer.toString(counter++), itemModel); populateItem(item); return item; } /** * Populate a given item. *

 

* be careful to add any components to the list item. So, don't do: * *
	 * add(new Label("foo", "bar"));
	 * 
* * but: * *
	 * item.add(new Label("foo", "bar"));
	 * 
* *

 

* * @param item * The item to populate */ protected abstract void populateItem(ListSetItem item); @SuppressWarnings("unchecked") private ListOrderedSet getList(){ return (ListOrderedSet) getDefaultModelObject(); } private void updateAjax(AjaxRequestTarget target){ Set> newItems = new HashSet>(); Set> removedItems = new HashSet>(); updateItems(newItems, removedItems); for(ListSetItem item : removedItems){ target.prependJavaScript("$('#"+item.getMarkupId()+"').remove();"); } for(ListSetItem item : newItems){ String script = String.format( "var item = document.createElement('%s'); " + "item.id = '%s'; "+ "var container = Wicket.$('%s'); " + "$(container).append(item); " , markupTag, item.getMarkupId(), getMarkupId()); target.prependJavaScript(script); target.add(item); } for(T el : getList()){ ListSetItem item = elementToComponent.get(el); target.appendJavaScript("$('#"+item.getMarkupId()+"').appendTo('#"+getMarkupId()+"');"); } } /** * FOR {@link AjaxListSetView} INTERNAL USE * * @author acamilo */ public static class AjaxListener implements IListener { @Override public void onBeforeRespond(Map map, AjaxRequestTarget target) { Set toRemove = new HashSet(); for(Map.Entry entry : new HashMap(map).entrySet()){ if(entry.getValue() instanceof AjaxListSetView){ AjaxListSetView listView = (AjaxListSetView) entry.getValue(); listView.updateAjax(target); toRemove.add(entry.getKey()); } } for(String key : toRemove){ map.remove(key); } } @Override public void onAfterRespond(Map map, IJavaScriptResponse response) { } @Override public void updateAjaxAttributes(AbstractDefaultAjaxBehavior behavior, AjaxRequestAttributes attributes) { } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy