From 994e86d36b6a86c084fcfcb087de8b1503b0d519 Mon Sep 17 00:00:00 2001 From: reiern70 Date: Tue, 8 Nov 2022 13:22:52 +0700 Subject: [PATCH] allows to with an ID instead of index in order to synchronize client and server side. This also would allow to not need to keep in memory the choices. --- .../AbstractAutoCompleteTextField.java | 437 ++++++++++++++++++ .../autocomplete/AutoCompleteBehavior.java | 18 +- .../AutoCompleteChoiceModelBehavior.java | 13 +- .../autocomplete/AutoCompleteTextField.java | 378 ++------------- .../autocomplete/IAutoCompleteHandler.java | 62 +++ .../autocomplete/IAutoCompleteListener.java | 4 +- 6 files changed, 569 insertions(+), 343 deletions(-) create mode 100644 wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java create mode 100644 wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/IAutoCompleteHandler.java diff --git a/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java b/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java new file mode 100644 index 0000000000..75cb6be858 --- /dev/null +++ b/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java @@ -0,0 +1,437 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.googlecode.wicket.jquery.ui.form.autocomplete; + +import com.googlecode.wicket.jquery.core.IJQueryWidget; +import com.googlecode.wicket.jquery.core.JQueryBehavior; +import com.googlecode.wicket.jquery.core.renderer.ITextRenderer; +import com.googlecode.wicket.jquery.core.renderer.TextRenderer; +import com.googlecode.wicket.jquery.core.template.IJQueryTemplate; +import com.googlecode.wicket.jquery.core.utils.RequestCycleUtils; +import com.googlecode.wicket.jquery.ui.template.JQueryTemplateBehavior; +import org.apache.wicket.Component; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.markup.ComponentTag; +import org.apache.wicket.markup.head.IHeaderResponse; +import org.apache.wicket.markup.head.JavaScriptHeaderItem; +import org.apache.wicket.markup.head.OnDomReadyHeaderItem; +import org.apache.wicket.markup.html.form.TextField; +import org.apache.wicket.model.IModel; +import org.apache.wicket.request.resource.JavaScriptResourceReference; +import org.apache.wicket.util.convert.IConverter; + +import java.io.Serializable; +import java.util.List; +import java.util.Locale; + +/** + * Provides a jQuery auto-complete widget + * + * @param the type of the model object + * @author Sebastien Briquet - sebfz1 + */ +public abstract class AbstractAutoCompleteTextField extends TextField implements IJQueryWidget, IAutoCompleteListener // NOSONAR +{ + private static final long serialVersionUID = 1L; + + private static final JavaScriptResourceReference JS = new JavaScriptResourceReference(AbstractAutoCompleteTextField.class, "AutoCompleteTextField.js"); + + /** + * Behavior that will be called when the user enters an input + */ + private AutoCompleteChoiceModelBehavior choiceModelBehavior; + + private final ITextRenderer renderer; + private final IConverter converter; + + private final IAutoCompleteHandler handler; + + private final IJQueryTemplate template; + private JQueryTemplateBehavior templateBehavior = null; + + + /** + * Setting this flag to true adds some extra protection at client side + * preventing users to be able to select stale elements. + */ + private boolean preventSelectWhileQueryIsRunning = false; + + private AutoCompleteBehavior autoCompleteBehavior; + + /** + * Constructor + * + * @param id the markup id + */ + public AbstractAutoCompleteTextField(String id) + { + this(id, new TextRenderer<>()); + } + + /** + * Constructor + * + * @param id the markup id + * @param type the type of the bean. This parameter should be supplied for the internal converter ({@link #getConverter(Class)}) to be used. + */ + public AbstractAutoCompleteTextField(String id, Class type) + { + this(id, new TextRenderer<>(), type); + } + + /** + * Constructor + * + * @param id the markup id + * @param renderer the {@link ITextRenderer} + */ + public AbstractAutoCompleteTextField(String id, ITextRenderer renderer) + { + this(id, renderer, null); + } + + /** + * Constructor + * + * @param id the markup id + * @param renderer the {@link ITextRenderer} + * @param type the type of the bean. This parameter should be supplied for the internal converter ({@link #getConverter(Class)}) to be used. + */ + public AbstractAutoCompleteTextField(String id, ITextRenderer renderer, Class type) + { + super(id, type); + + this.renderer = renderer; + this.template = this.newTemplate(); + this.converter = this.newConverter(); + this.handler = this.newHandler(); + } + + /** + * Constructor + * + * @param id the markup id + * @param model the {@link IModel} + */ + public AbstractAutoCompleteTextField(String id, IModel model) + { + this(id, model, new TextRenderer<>(), null); + } + + /** + * Constructor + * + * @param id the markup id + * @param model the {@link IModel} + * @param type the type of the bean. This parameter should be supplied for the internal converter ({@link #getConverter(Class)}) to be used. + */ + public AbstractAutoCompleteTextField(String id, IModel model, Class type) + { + this(id, model, new TextRenderer<>(), type); + } + + /** + * Constructor + * + * @param id the markup id + * @param model the {@link IModel} + * @param renderer the {@link ITextRenderer} + */ + public AbstractAutoCompleteTextField(String id, IModel model, ITextRenderer renderer) + { + this(id, model, renderer, null); + } + + /** + * Constructor + * + * @param id the markup id + * @param model the {@link IModel} + * @param renderer the {@link ITextRenderer} + * @param type the type of the bean. This parameter should be supplied for the internal converter ({@link #getConverter(Class)}) to be used. + */ + public AbstractAutoCompleteTextField(String id, IModel model, ITextRenderer renderer, Class type) + { + super(id, model, type); + + this.renderer = renderer; + this.template = this.newTemplate(); + this.converter = this.newConverter(); + this.handler = this.newHandler(); + } + + // Methods // + + @Override + protected final String getModelValue() + { + return this.renderer.getText(this.getModelObject()); // renderer cannot be null. + } + + @Override + @SuppressWarnings("unchecked") + public IConverter getConverter(Class type) + { + // TODO: manage String (property)model object in a better way + if (!String.class.isAssignableFrom(this.getType())) + { + if (type != null && type.isAssignableFrom(this.getType())) + { + return (IConverter) this.converter; + } + } + + return super.getConverter(type); + } + + /** + * Gets the template script token/id + * + * @return the template script token/id + */ + public String getTemplateToken() + { + if (this.templateBehavior != null) + { + return this.templateBehavior.getToken(); + } + + return null; + } + + // Properties // + + /** + * Gets the {@link ITextRenderer} + * + * @return the {@link ITextRenderer} + */ + public ITextRenderer getRenderer() + { + return this.renderer; + } + + // Events // + + @Override + protected void onInitialize() + { + super.onInitialize(); + + this.choiceModelBehavior = this.newChoiceModelBehavior(); + this.add(this.choiceModelBehavior); + + this.add(JQueryWidget.newWidgetBehavior(this)); // cannot be in ctor as the markupId may be set manually afterward + + if (this.template != null) + { + this.templateBehavior = new JQueryTemplateBehavior(this.template); + this.add(this.templateBehavior); + } + } + + @Override + public void onConfigure(JQueryBehavior behavior) + { + // noop + } + + @Override + public void onBeforeRender(JQueryBehavior behavior) + { + } + + @Override + protected void onComponentTag(final ComponentTag tag) + { + super.onComponentTag(tag); + + tag.put("autocomplete", "off"); // disable browser's autocomplete + } + + @Override + public final void onSelect(AjaxRequestTarget target, I index) + { + T selected = handler.selectChoice(index); + if (selected != null) { + this.setModelObject(selected); + this.onSelected(target); + } else { + this.onSelectionFailed(target, index); + } + } + + /** + * Triggered when the user selects an item from results that matched its input + * + * @param target the {@link AjaxRequestTarget} + */ + protected void onSelected(AjaxRequestTarget target) + { + } + + /** + * Called when teh selection failed. + * + * @param target the {@link AjaxRequestTarget} + * @param index the selected index + */ + protected void onSelectionFailed(AjaxRequestTarget target, I index) + { + } + + // IJQueryWidget // + + @Override + public JQueryBehavior newWidgetBehavior(String selector) + { + return autoCompleteBehavior = new AutoCompleteBehavior<>(selector, this) { // NOSONAR + + private static final long serialVersionUID = 1L; + + @Override + protected CharSequence getChoiceCallbackUrl() + { + return choiceModelBehavior.getCallbackUrl(); + } + + @Override + protected I indexToId(String index) { + return handler.indexToId(index); + } + + @Override + protected String $() + { + if (templateBehavior != null) + { + // warning, the template text should be of the form ... in order to work + String render = "jQuery('%s').data('ui-autocomplete')._renderItem = function( ul, item ) { " // lf + + "var content = jQuery.tmpl(jQuery('#%s').html(), item);" // lf + + "return jQuery('
  • ').data('ui-autocomplete-item', item).append(content).appendTo(ul);" // lf + + "}"; + + return super.$() + String.format(render, this.selector, templateBehavior.getToken()); + } + + return super.$(); + } + + @Override + public void onConfigure(Component component) { + super.onConfigure(component); + if (preventSelectWhileQueryIsRunning) { + // we define special functions in for handling select and fetch data + this.setOption("select", "function(request, response) {\n" + getVarName() + ".select(request, response); \n}"); + if (this.isEnabled(component)) + { + this.setOption("source", "function(event, ui) {\n" + getVarName() + ".fetchItems(event, ui); \n}"); + } + } + } + }; + } + + public void renderHead(IHeaderResponse response) { + if (preventSelectWhileQueryIsRunning) { + response.render(JavaScriptHeaderItem.forReference(JS)); + // we create a prototype with the data we need + response.render(OnDomReadyHeaderItem.forScript(getVarName() + " = new WJQUI.AutoComplete('" + + getMarkupId() + "','" + choiceModelBehavior.getCallbackUrl() + + "', '" + autoCompleteBehavior.getOnSelectAjaxBehavior().getCallbackUrl() + "');")); + } + } + + private String getVarName() { + return "window.aut_" + getMarkupId(); + } + + public AbstractAutoCompleteTextField setPreventSelectWhileQueryIsRunning(boolean preventSelectWhileQueryIsRunning) { + this.preventSelectWhileQueryIsRunning = preventSelectWhileQueryIsRunning; + return this; + } + + + // Factories // + + /** + * Gets a new {@link IJQueryTemplate} to customize the rendering
    + * The {@link IJQueryTemplate#getText()} should return a template text of the form "<a>...</a>".
    + * The properties used in the template text (ie: ${name}) should be identified in the list returned by {@link IJQueryTemplate#getTextProperties()} + * + * @return null by default + */ + protected IJQueryTemplate newTemplate() + { + return null; + } + + protected abstract IAutoCompleteHandler newHandler(); + + /** + * Gets a new {@link IConverter}.
    + * Used when the form component is posted and the bean type has been supplied to the constructor. + * + * @return the {@link IConverter} + */ + private IConverter newConverter() + { + return new IConverter<>() { // NOSONAR + + private static final long serialVersionUID = 1L; + + @Override + public T convertToObject(String value, Locale locale) + { + if (value != null && value.equals(AbstractAutoCompleteTextField.this.getModelValue())) + { + return AbstractAutoCompleteTextField.this.getModelObject(); + } + + return null; // if the TextField value (string) does not corresponds to the current object model (ie: user specific value), returns null. + } + + @Override + public String convertToString(T value, Locale locale) + { + return AbstractAutoCompleteTextField.this.renderer.getText(value); + } + }; + } + + /** + * Gets a new {@link AutoCompleteChoiceModelBehavior} + * + * @return the {@link AutoCompleteChoiceModelBehavior} + */ + private AutoCompleteChoiceModelBehavior newChoiceModelBehavior() + { + return new AutoCompleteChoiceModelBehavior<>(this.renderer, this.template, this.handler) { // NOSONAR + + private static final long serialVersionUID = 1L; + private static final String TERM = "term"; + + @Override + public List getChoices() + { + final String input = RequestCycleUtils.getQueryParameterValue(TERM).toString(); + + return handler.getChoices(input); + } + }; + } +} diff --git a/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/AutoCompleteBehavior.java b/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/AutoCompleteBehavior.java index 86a373b2bc..9e0ea680f0 100644 --- a/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/AutoCompleteBehavior.java +++ b/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/AutoCompleteBehavior.java @@ -33,13 +33,13 @@ * * @author Sebastien Briquet - sebfz1 */ -public abstract class AutoCompleteBehavior extends JQueryUIBehavior implements IJQueryAjaxAware +public abstract class AutoCompleteBehavior extends JQueryUIBehavior implements IJQueryAjaxAware { private static final long serialVersionUID = 1L; public static final String METHOD = "autocomplete"; /** event listener */ - private final IAutoCompleteListener listener; + private final IAutoCompleteListener listener; private JQueryAjaxBehavior onSelectAjaxBehavior = null; @@ -49,7 +49,7 @@ public abstract class AutoCompleteBehavior extends JQueryUIBehavior implements I * @param selector the html selector (ie: "#myId") * @param listener the {@link IAutoCompleteListener} */ - public AutoCompleteBehavior(String selector, IAutoCompleteListener listener) + public AutoCompleteBehavior(String selector, IAutoCompleteListener listener) { this(selector, new Options(), listener); } @@ -61,7 +61,7 @@ public AutoCompleteBehavior(String selector, IAutoCompleteListener listener) * @param options the {@link Options} * @param listener the {@link IAutoCompleteListener} */ - public AutoCompleteBehavior(String selector, Options options, IAutoCompleteListener listener) + public AutoCompleteBehavior(String selector, Options options, IAutoCompleteListener listener) { super(selector, METHOD, options); @@ -111,10 +111,12 @@ public void onAjax(AjaxRequestTarget target, JQueryEvent event) { if (event instanceof SelectEvent) { - this.listener.onSelect(target, ((SelectEvent) event).getIndex()); + this.listener.onSelect(target, indexToId(((SelectEvent) event).getIndex())); } } + protected abstract I indexToId(String index); + // Factories // /** @@ -164,14 +166,14 @@ protected JQueryEvent newEvent() */ protected static class SelectEvent extends JQueryEvent { - private final int index; + private final String index; public SelectEvent() { - this.index = RequestCycleUtils.getQueryParameterValue("index").toInt(0); + this.index = RequestCycleUtils.getQueryParameterValue("index").toString(null); } - public int getIndex() + public String getIndex() { return this.index; } diff --git a/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/AutoCompleteChoiceModelBehavior.java b/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/AutoCompleteChoiceModelBehavior.java index f42cdc5b1c..1519f23fb4 100644 --- a/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/AutoCompleteChoiceModelBehavior.java +++ b/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/AutoCompleteChoiceModelBehavior.java @@ -33,18 +33,22 @@ * @param the model object type * @author Sebastien Briquet - sebfz1 */ -abstract class AutoCompleteChoiceModelBehavior extends ChoiceModelBehavior +abstract class AutoCompleteChoiceModelBehavior extends ChoiceModelBehavior { private static final long serialVersionUID = 1L; - public AutoCompleteChoiceModelBehavior(ITextRenderer renderer) + private final IAutoCompleteHandler handler; + + public AutoCompleteChoiceModelBehavior(ITextRenderer renderer, IAutoCompleteHandler handler) { super(renderer); + this.handler = handler; } - public AutoCompleteChoiceModelBehavior(ITextRenderer renderer, IJQueryTemplate template) + public AutoCompleteChoiceModelBehavior(ITextRenderer renderer, IJQueryTemplate template, IAutoCompleteHandler handler) { super(renderer, template); + this.handler = handler; } @Override @@ -61,7 +65,8 @@ protected String getResponse(IRequestParameters parameters) // ITextRenderer // final JSONObject object = this.renderer.render(choice); - object.put("id", Integer.toString(index)); /* 'id' is a reserved word */ + object.put("id", handler.getId(choice, index)); /* 'id' is a reserved word */ + object.put("index", Integer.toString(index)); /* 'id' is a reserved word */ object.put("value", this.renderer.getText(choice)); /* 'value' is a reserved word */ // Additional properties (like template properties) // diff --git a/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/AutoCompleteTextField.java b/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/AutoCompleteTextField.java index 554b32647e..cadb5d112a 100644 --- a/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/AutoCompleteTextField.java +++ b/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/AutoCompleteTextField.java @@ -46,227 +46,94 @@ * @param the type of the model object * @author Sebastien Briquet - sebfz1 */ -public abstract class AutoCompleteTextField extends TextField implements IJQueryWidget, IAutoCompleteListener // NOSONAR +public abstract class AutoCompleteTextField extends AbstractAutoCompleteTextField // NOSONAR { private static final long serialVersionUID = 1L; - private static final JavaScriptResourceReference JS = new JavaScriptResourceReference(AutoCompleteTextField.class, "AutoCompleteTextField.js"); - /** - * Behavior that will be called when the user enters an input - */ - private AutoCompleteChoiceModelBehavior choiceModelBehavior; - - private final ITextRenderer renderer; - private final IConverter converter; + private abstract class IndexIAutoCompleteHandler implements IAutoCompleteHandler { - private final IJQueryTemplate template; - private JQueryTemplateBehavior templateBehavior = null; - - /** - * Cache of current choices, needed to retrieve the user selected object - */ - private List choices; + @Override + public Integer getId(T choice, int index) + { + return index; + } + @Override + public Integer indexToId(String index) { + try { + return Integer.parseInt(index); + } catch (NumberFormatException e) { + return -1; + } - /** - * Setting this flag to true adds some extra protection at client side - * preventing users to be able to select stale elements. - */ - private boolean preventSelectWhileQueryIsRunning = false; + } - private AutoCompleteBehavior autoCompleteBehavior; + @Override + public T selectChoice(Integer index) { + if (-1 < index && index < choices.size()) + { + return choices.get(index); + } + return null; + } + } - /** - * Constructor - * - * @param id the markup id - */ public AutoCompleteTextField(String id) { - this(id, new TextRenderer(), null); + super(id); } - /** - * Constructor - * - * @param id the markup id - * @param type the type of the bean. This parameter should be supplied for the internal converter ({@link #getConverter(Class)}) to be used. - */ public AutoCompleteTextField(String id, Class type) { - this(id, new TextRenderer(), type); + super(id, type); } - /** - * Constructor - * - * @param id the markup id - * @param renderer the {@link ITextRenderer} - */ public AutoCompleteTextField(String id, ITextRenderer renderer) { - this(id, renderer, null); + super(id, renderer); } - /** - * Constructor - * - * @param id the markup id - * @param renderer the {@link ITextRenderer} - * @param type the type of the bean. This parameter should be supplied for the internal converter ({@link #getConverter(Class)}) to be used. - */ public AutoCompleteTextField(String id, ITextRenderer renderer, Class type) { - super(id, type); - - this.renderer = renderer; - this.template = this.newTemplate(); - this.converter = this.newConverter(); + super(id, renderer, type); } - /** - * Constructor - * - * @param id the markup id - * @param model the {@link IModel} - */ public AutoCompleteTextField(String id, IModel model) { - this(id, model, new TextRenderer(), null); + super(id, model); } - /** - * Constructor - * - * @param id the markup id - * @param model the {@link IModel} - * @param type the type of the bean. This parameter should be supplied for the internal converter ({@link #getConverter(Class)}) to be used. - */ public AutoCompleteTextField(String id, IModel model, Class type) { - this(id, model, new TextRenderer(), type); + super(id, model, type); } - /** - * Constructor - * - * @param id the markup id - * @param model the {@link IModel} - * @param renderer the {@link ITextRenderer} - */ public AutoCompleteTextField(String id, IModel model, ITextRenderer renderer) { - this(id, model, renderer, null); - } - - /** - * Constructor - * - * @param id the markup id - * @param model the {@link IModel} - * @param renderer the {@link ITextRenderer} - * @param type the type of the bean. This parameter should be supplied for the internal converter ({@link #getConverter(Class)}) to be used. - */ - public AutoCompleteTextField(String id, IModel model, ITextRenderer renderer, Class type) - { - super(id, model, type); - - this.renderer = renderer; - this.template = this.newTemplate(); - this.converter = this.newConverter(); + super(id, model, renderer); } - // Methods // - - /** - * Call {@link #getChoices(String)} and cache the result
    - * Internal use only - * - * @param input String that represent the query - * @return the list of choices - */ - private List internalGetChoices(String input) + public AutoCompleteTextField(String id, IModel model, TextRenderer renderer, Class tClass) { - this.choices = this.getChoices(input); - - return this.choices; + super(id, model, renderer, tClass); } /** - * Gets choices matching the provided input - * - * @param input String that represent the query - * @return the list of choices + * Cache of current choices, needed to retrieve the user selected object */ - protected abstract List getChoices(String input); + private List choices; - @Override - protected final String getModelValue() - { - return this.renderer.getText(this.getModelObject()); // renderer cannot be null. - } @Override - @SuppressWarnings("unchecked") - public IConverter getConverter(Class type) - { - // TODO: manage String (property)model object in a better way - if (!String.class.isAssignableFrom(this.getType())) - { - if (type != null && type.isAssignableFrom(this.getType())) - { - return (IConverter) this.converter; + protected IAutoCompleteHandler newHandler() { + return new IndexIAutoCompleteHandler() { + @Override + public List getChoices(String term) { + AutoCompleteTextField.this.choices = AutoCompleteTextField.this.getChoices(term); + return AutoCompleteTextField.this.choices; } - } - - return super.getConverter(type); - } - - /** - * Gets the template script token/id - * - * @return the template script token/id - */ - public String getTemplateToken() - { - if (this.templateBehavior != null) - { - return this.templateBehavior.getToken(); - } - - return null; - } - - // Properties // - - /** - * Gets the {@link ITextRenderer} - * - * @return the {@link ITextRenderer} - */ - public ITextRenderer getRenderer() - { - return this.renderer; - } - - // Events // - - @Override - protected void onInitialize() - { - super.onInitialize(); - - this.choiceModelBehavior = this.newChoiceModelBehavior(); - this.add(this.choiceModelBehavior); - - this.add(JQueryWidget.newWidgetBehavior(this)); // cannot be in ctor as the markupId may be set manually afterward - - if (this.template != null) - { - this.templateBehavior = new JQueryTemplateBehavior(this.template); - this.add(this.templateBehavior); - } + }; } @Override @@ -280,166 +147,19 @@ public void onBeforeRender(JQueryBehavior behavior) { } - @Override - protected void onComponentTag(final ComponentTag tag) - { - super.onComponentTag(tag); - - tag.put("autocomplete", "off"); // disable browser's autocomplete - } - - @Override - public final void onSelect(AjaxRequestTarget target, int index) - { - if (-1 < index && index < this.choices.size()) - { - T choice = this.choices.get(index); - - this.setModelObject(choice); - this.onSelected(target); - } - } - /** - * Triggered when the user selects an item from results that matched its input + * Gets choices matching the provided input * - * @param target the {@link AjaxRequestTarget} + * @param input String that represent the query + * @return the list of choices */ - protected void onSelected(AjaxRequestTarget target) - { - } - - // IJQueryWidget // + protected abstract List getChoices(String input); @Override - public JQueryBehavior newWidgetBehavior(String selector) - { - return autoCompleteBehavior = new AutoCompleteBehavior(selector, this) { // NOSONAR - - private static final long serialVersionUID = 1L; - - @Override - protected CharSequence getChoiceCallbackUrl() - { - return choiceModelBehavior.getCallbackUrl(); - } - - @Override - protected String $() - { - if (templateBehavior != null) - { - // warning, the template text should be of the form ... in order to work - String render = "jQuery('%s').data('ui-autocomplete')._renderItem = function( ul, item ) { " // lf - + "var content = jQuery.tmpl(jQuery('#%s').html(), item);" // lf - + "return jQuery('
  • ').data('ui-autocomplete-item', item).append(content).appendTo(ul);" // lf - + "}"; - - return super.$() + String.format(render, this.selector, templateBehavior.getToken()); - } - - return super.$(); - } - - @Override - public void onConfigure(Component component) { - super.onConfigure(component); - if (preventSelectWhileQueryIsRunning) { - // we define special functions in for handling select and fetch data - this.setOption("select", "function(request, response) {\n" + getVarName() + ".select(request, response); \n}"); - if (this.isEnabled(component)) - { - this.setOption("source", "function(event, ui) {\n" + getVarName() + ".fetchItems(event, ui); \n}"); - } - } - } - }; - } - - public void renderHead(IHeaderResponse response) { - if (preventSelectWhileQueryIsRunning) { - response.render(JavaScriptHeaderItem.forReference(JS)); - // we create a prototype with the data we need - response.render(OnDomReadyHeaderItem.forScript(getVarName() + " = new WJQUI.AutoComplete('" - + getMarkupId() + "','" + choiceModelBehavior.getCallbackUrl() - + "', '" + autoCompleteBehavior.getOnSelectAjaxBehavior().getCallbackUrl() + "');")); - } - } - - private String getVarName() { - return "window.aut_" + getMarkupId(); - } - - public AutoCompleteTextField setPreventSelectWhileQueryIsRunning(boolean preventSelectWhileQueryIsRunning) { - this.preventSelectWhileQueryIsRunning = preventSelectWhileQueryIsRunning; - return this; - } - - - // Factories // - - /** - * Gets a new {@link IJQueryTemplate} to customize the rendering
    - * The {@link IJQueryTemplate#getText()} should return a template text of the form "<a>...</a>".
    - * The properties used in the template text (ie: ${name}) should be identified in the list returned by {@link IJQueryTemplate#getTextProperties()} - * - * @return null by default - */ - protected IJQueryTemplate newTemplate() - { - return null; - } - - /** - * Gets a new {@link IConverter}.
    - * Used when the form component is posted and the bean type has been supplied to the constructor. - * - * @return the {@link IConverter} - */ - private IConverter newConverter() - { - return new IConverter() { // NOSONAR - - private static final long serialVersionUID = 1L; - - @Override - public T convertToObject(String value, Locale locale) - { - if (value != null && value.equals(AutoCompleteTextField.this.getModelValue())) - { - return AutoCompleteTextField.this.getModelObject(); - } - - return null; // if the TextField value (string) does not corresponds to the current object model (ie: user specific value), returns null. - } - - @Override - public String convertToString(T value, Locale locale) - { - return AutoCompleteTextField.this.renderer.getText(value); - } - }; - } - - /** - * Gets a new {@link AutoCompleteChoiceModelBehavior} - * - * @return the {@link AutoCompleteChoiceModelBehavior} - */ - private AutoCompleteChoiceModelBehavior newChoiceModelBehavior() + protected void onComponentTag(final ComponentTag tag) { - return new AutoCompleteChoiceModelBehavior(this.renderer, this.template) { // NOSONAR - - private static final long serialVersionUID = 1L; - private static final String TERM = "term"; - - @Override - public List getChoices() - { - final String input = RequestCycleUtils.getQueryParameterValue(TERM).toString(); + super.onComponentTag(tag); - return AutoCompleteTextField.this.internalGetChoices(input); - } - }; + tag.put("autocomplete", "off"); // disable browser's autocomplete } } diff --git a/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/IAutoCompleteHandler.java b/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/IAutoCompleteHandler.java new file mode 100644 index 0000000000..71fac66fd5 --- /dev/null +++ b/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/IAutoCompleteHandler.java @@ -0,0 +1,62 @@ +package com.googlecode.wicket.jquery.ui.form.autocomplete; + +import java.util.List; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This interface defines a way to use autocomplete based in other selection criteria not necessarily the index + * + * @param The type of bean + * @param The type of the ID generate + */ +public interface IAutoCompleteHandler +{ + /** + * Returns a unique id for a choice with a given index. + * + * @param choice The choice + * @param index The index + * + * @return a given ID + */ + ID getId(T choice, int index); + + /** + * Converts a client side "index" into an ID. + * + * @param index The index + * @return the server side ID corresponding to the index. + */ + ID indexToId(String index); + + /** + * @param term The filtering term + * @return A list of choices matching the term. + */ + List getChoices(String term); + + + /** + * Selects the choice for the given ID (if any still exists) + * + * @param id The ID + * @return The selected choice. + */ + T selectChoice(ID id); +} diff --git a/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/IAutoCompleteListener.java b/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/IAutoCompleteListener.java index 9c0d4c2ed8..99a601dbea 100644 --- a/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/IAutoCompleteListener.java +++ b/wicket-jquery-ui/src/main/java/com/googlecode/wicket/jquery/ui/form/autocomplete/IAutoCompleteListener.java @@ -25,7 +25,7 @@ * @author Sebastien Briquet - sebfz1 * */ -public interface IAutoCompleteListener extends IClusterable +public interface IAutoCompleteListener extends IClusterable { /** * Triggered when a selection has been made @@ -33,5 +33,5 @@ public interface IAutoCompleteListener extends IClusterable * @param target the {@link AjaxRequestTarget} * @param index the index of the selected item */ - void onSelect(AjaxRequestTarget target, int index); + void onSelect(AjaxRequestTarget target, I index); }