001 package net.databinder.components.hib;
002
003 import net.databinder.components.AjaxOnKeyPausedUpdater;
004 import net.databinder.components.AjaxCell;
005 import net.databinder.models.hib.PropertyQueryBinder;
006 import net.databinder.models.hib.QueryBinder;
007
008 import org.apache.wicket.ResourceReference;
009 import org.apache.wicket.ajax.AjaxRequestTarget;
010 import org.apache.wicket.ajax.markup.html.AjaxLink;
011 import org.apache.wicket.markup.html.form.Form;
012 import org.apache.wicket.markup.html.form.TextField;
013 import org.apache.wicket.markup.html.image.Image;
014 import org.apache.wicket.markup.html.panel.Panel;
015 import org.apache.wicket.model.CompoundPropertyModel;
016
017 /**
018 * Panel for a "live" search field with a clear button. Instances of this class must
019 * implement the onUpdate method to register external components for updating.
020 * It is possible to override the search button text with the key "searchbutton.text"
021 * @author Nathan Hamblen
022 */
023 public abstract class SearchPanel extends Panel {
024
025 private String searchInput;
026
027 /**
028 * @param id Wicket id
029 */
030 public SearchPanel(String id) {
031 super(id);
032 add(new SearchForm("searchForm"));
033 }
034
035
036 /**
037 * Override to add components to be updated (or JavaScript to be executed)
038 * when the search string changes. Remember that added components must
039 * have a markup id; use component.setMarkupId(true) to assign one
040 * programmatically.
041 * @param target Ajax target to register components for update
042 */
043 public abstract void onUpdate(AjaxRequestTarget target);
044
045 /**
046 * Binds the search model to a "search" parameter in a query. The value in the
047 * search field will be bracketed by percent signs (%) for a find-anywhere match.
048 * In the query itself, "search" must be the name of the one and only parameter
049 * If your needs differ, bind the model passed in to the SearchPanel constructor
050 * to your own IQueryBinder instance; this is a convenience method.
051 * @return binder for a "search" parameter
052 */
053 public QueryBinder getQueryBinder() {
054 return new PropertyQueryBinder(this);
055 }
056
057 public String getSearch() {
058 return searchInput == null ? searchInput : "%" + searchInput + "%";
059 }
060
061 /** Form with AJAX components and their AjaxCells. */
062 public class SearchForm extends Form {
063 public SearchForm(String id) {
064 super(id, new CompoundPropertyModel(SearchPanel.this));
065
066 final AjaxCell searchWrap = new AjaxCell("searchWrap");
067 add(searchWrap);
068 final TextField search = new TextField("searchInput");
069 search.setOutputMarkupId(true);
070 searchWrap.add(search);
071
072 final AjaxCell clearWrap = new AjaxCell("clearWrap");
073 add(clearWrap);
074 final AjaxLink clearLink = new AjaxLink("clearLink") {
075 /** Clear field and register updates. */
076 public void onClick(AjaxRequestTarget target) {
077 search.setModelObject(null);
078 target.addComponent(searchWrap);
079 target.addComponent(clearWrap);
080 SearchPanel.this.onUpdate(target);
081 }
082 /** Hide when search is blank. */
083 public boolean isVisible() {
084 return search.getModelObject() != null;
085 }
086 };
087 clearLink.setOutputMarkupId(true);
088 clearLink.add( new Image("clear",
089 new ResourceReference(this.getClass(), "clear.png")));
090 clearWrap.add(clearLink);
091
092 // triggered when user pauses or tabs out
093 search.add(new AjaxOnKeyPausedUpdater() {
094 protected void onUpdate(AjaxRequestTarget target) {
095 target.addComponent(clearWrap);
096 SearchPanel.this.onUpdate(target);
097 }
098 });
099 }
100 }
101 }