From fa7d56f8c90cf3ed596c457c54fee9d429d63bc0 Mon Sep 17 00:00:00 2001 From: reiern70 Date: Thu, 27 Aug 2026 19:40:13 -0500 Subject: [PATCH 1/7] make selection machinery pluggable so that for instance we can use an ID based mechanism --- .../jquery/core/ajax/JQueryAjaxBehavior.java | 2 +- .../jquery/core/utils/ListUtils.java | 2 +- .../AbstractAutoCompleteTextField.java | 62 +++++++++---- .../autocomplete/AutoCompleteBehavior.java | 43 ++++++---- .../AutoCompleteChoiceModelBehavior.java | 13 ++- .../autocomplete/IAutoCompleteListener.java | 37 +++++++- .../IElementSelectionStrategy.java | 55 ++++++++++++ .../IdBasedElementSelectionStrategy.java | 68 +++++++++++++++ .../IndexBasedElementSelectionStrategy.java | 86 +++++++++++++++++++ ...PropertyBasedElementSelectionStrategy.java | 56 ++++++++++++ 10 files changed, 388 insertions(+), 36 deletions(-) create mode 100644 wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IElementSelectionStrategy.java create mode 100644 wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IdBasedElementSelectionStrategy.java create mode 100644 wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IndexBasedElementSelectionStrategy.java create mode 100644 wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/PropertyBasedElementSelectionStrategy.java diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-core/src/main/java/org/wicketstuff/jquery/core/ajax/JQueryAjaxBehavior.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-core/src/main/java/org/wicketstuff/jquery/core/ajax/JQueryAjaxBehavior.java index 5feac31056..28e1bfb567 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-core/src/main/java/org/wicketstuff/jquery/core/ajax/JQueryAjaxBehavior.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-core/src/main/java/org/wicketstuff/jquery/core/ajax/JQueryAjaxBehavior.java @@ -157,7 +157,7 @@ public JQueryAjaxBehavior(IJQueryAjaxAware source) * Constructor * * @param source {@link Behavior} to which the event - returned by {@link #newEvent()} - will be broadcasted. - * @param duration {@link Duration}. If different than {@link Duration#NONE}, an {@link ThrottlingSettings} will be added with the specified {@link Duration}. + * @param duration {@link Duration}. If different than {@link Duration#ZERO}, an {@link ThrottlingSettings} will be added with the specified {@link Duration}. */ public JQueryAjaxBehavior(IJQueryAjaxAware source, Duration duration) { diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-core/src/main/java/org/wicketstuff/jquery/core/utils/ListUtils.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-core/src/main/java/org/wicketstuff/jquery/core/utils/ListUtils.java index 95d2a728f2..aa58d7a869 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-core/src/main/java/org/wicketstuff/jquery/core/utils/ListUtils.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-core/src/main/java/org/wicketstuff/jquery/core/utils/ListUtils.java @@ -313,7 +313,7 @@ public static boolean isEmpty(List list) /** * Converts a {@link StringValue} to a list of {@code String} * - * @param value the {@link StringValue} + * @param values the {@link StringValue} * @return a list of {@code String} */ public static List toStringList(StringValue values) diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java index 6b66866bae..3297fe8835 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java @@ -16,6 +16,7 @@ */ package org.wicketstuff.jquery.ui.form.autocomplete; +import java.util.Collections; import java.util.List; import java.util.Locale; import org.apache.wicket.ajax.AjaxRequestTarget; @@ -23,7 +24,6 @@ import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.model.IModel; import org.apache.wicket.util.convert.IConverter; -import org.apache.wicket.util.lang.Args; import org.wicketstuff.jquery.core.IJQueryWidget; import org.wicketstuff.jquery.core.JQueryBehavior; import org.wicketstuff.jquery.core.renderer.ITextRenderer; @@ -40,7 +40,7 @@ * @author Sebastien Briquet - sebfz1 * @author reiern70 */ -public abstract class AbstractAutoCompleteTextField extends TextField implements IJQueryWidget, IAutoCompleteListener // NOSONAR +public abstract class AbstractAutoCompleteTextField extends TextField implements IJQueryWidget, IAutoCompleteListener // NOSONAR { private static final long serialVersionUID = 1L; @@ -51,6 +51,7 @@ public abstract class AbstractAutoCompleteTextField extends TextField impl private final ITextRenderer renderer; private final IConverter converter; + private final IElementSelectionStrategy elementSelectionStrategy; private final IJQueryTemplate template; private JQueryAbstractTemplateBehavior templateBehavior = null; @@ -108,6 +109,7 @@ public AbstractAutoCompleteTextField(String id, ITextRenderer rendere this.renderer = renderer; this.template = this.newTemplate(); this.converter = this.newConverter(); + this.elementSelectionStrategy = this.newElementSelectionStrategy(); } /** @@ -145,7 +147,8 @@ public AbstractAutoCompleteTextField(String id, IModel model, ITextRenderer model, ITextRenderer getRenderer() return this.renderer; } + /** + * Gets the {@link IElementSelectionStrategy} used to identify and resolve selected choices. + * + * @return the selection strategy, never {@code null} + */ + public IElementSelectionStrategy getElementSelectionStrategy() + { + return this.elementSelectionStrategy; + } + // Events // @Override @@ -292,17 +306,14 @@ protected void onComponentTag(final ComponentTag tag) tag.put("autocomplete", "off"); // disable browser's autocomplete } - @Override - public final void onSelect(AjaxRequestTarget target, int index) - { - if (-1 < index && index < this.choices.getObject().size()) - { - T choice = this.choices.getObject().get(index); - - this.setModelObject(choice); - this.onSelected(target); - } - } + @Override + public void onSelect(AjaxRequestTarget target, T choice) { + if (choice != null) { + LOG.error("Cannot select choice: " + choice); + } + this.setModelObject(choice); + this.onSelected(target); + } /** * Triggered when the user selects an item from results that matched its input @@ -318,7 +329,12 @@ protected void onSelected(AjaxRequestTarget target) @Override public JQueryBehavior newWidgetBehavior(String selector) { - return new AutoCompleteBehavior(selector, this) { // NOSONAR + return new AutoCompleteBehavior(selector, this, new IModel>() { + @Override + public List getObject() { + return choices != null ? choices.getObject() : Collections.emptyList(); + } + }) { // NOSONAR private static final long serialVersionUID = 1L; @@ -394,6 +410,16 @@ public String convertToString(T value, Locale locale) }; } + /** + * Gets a new {@link IElementSelectionStrategy}. Index-based selection is used by default. + * + * @return the selection strategy + */ + protected IElementSelectionStrategy newElementSelectionStrategy() + { + return IndexBasedElementSelectionStrategy.get(); + } + /** * Gets a new {@link AutoCompleteChoiceModelBehavior} * @@ -406,6 +432,12 @@ private AutoCompleteChoiceModelBehavior newChoiceModelBehavior() private static final long serialVersionUID = 1L; private static final String TERM = "term"; + @Override + protected IElementSelectionStrategy getElementSelectionStrategy() + { + return AbstractAutoCompleteTextField.this.getElementSelectionStrategy(); + } + @Override public List getChoices() { diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteBehavior.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteBehavior.java index d2654d90b3..1534c982ec 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteBehavior.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteBehavior.java @@ -16,9 +16,12 @@ */ package org.wicketstuff.jquery.ui.form.autocomplete; +import java.util.List; + import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.attributes.CallbackParameter; +import org.apache.wicket.model.IModel; import org.apache.wicket.util.lang.Args; import org.wicketstuff.jquery.core.JQueryEvent; import org.wicketstuff.jquery.core.Options; @@ -32,40 +35,45 @@ * * @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; + + /** the model producing values */ + + private final IModel> valuesModel; private JQueryAjaxBehavior onSelectAjaxBehavior = null; /** * Constructor * - * @param selector the html selector (ie: "#myId") + * @param selector the HTML selector (ie: "#myId") * @param listener the {@link IAutoCompleteListener} */ - public AutoCompleteBehavior(String selector, IAutoCompleteListener listener) + public AutoCompleteBehavior(String selector, IAutoCompleteListener listener, IModel> valuesModel) { - this(selector, new Options(), listener); + this(selector, new Options(), listener, valuesModel); } /** * Constructor * - * @param selector the html selector (ie: "#myId") + * @param selector the HTML selector (ie: "#myId") * @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, IModel> valuesModel) { super(selector, METHOD, options); this.listener = Args.notNull(listener, "listener"); - } + this.valuesModel = valuesModel; + } // Methods // @@ -108,16 +116,16 @@ public void onConfigure(Component component) @Override public void onAjax(AjaxRequestTarget target, JQueryEvent event) { - if (event instanceof SelectEvent) + if (event instanceof SelectEvent selectEvent) { - this.listener.onSelect(target, ((SelectEvent) event).getIndex()); + this.listener.onSelect(target, listener.getElementSelectionStrategy().findChoice(valuesModel.getObject(), selectEvent.getIdentifier())); } } // Factories // /** - * Gets a new {@link JQueryAjaxBehavior} that will be wired to the 'select' event + * Gets a new {@link JQueryAjaxBehavior} that will be wired to the 'select' eventta * * @param source the {@link IJQueryAjaxAware} * @return a new {@code OnSelectAjaxBehavior} by default @@ -163,16 +171,21 @@ protected JQueryEvent newEvent() */ protected static class SelectEvent extends JQueryEvent { - private final int index; + private final String identifier; public SelectEvent() { - this.index = RequestCycleUtils.getQueryParameterValue("index").toInt(0); + this.identifier = RequestCycleUtils.getQueryParameterValue("index").toString(); } - public int getIndex() + /** + * Gets the selected item identifier (JSON {@code id}). For the default index strategy this is the list index. + * + * @return the identifier + */ + public String getIdentifier() { - return this.index; + return this.identifier; } } } diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteChoiceModelBehavior.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteChoiceModelBehavior.java index 619e4c3829..f849e5106d 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteChoiceModelBehavior.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteChoiceModelBehavior.java @@ -47,6 +47,16 @@ public AutoCompleteChoiceModelBehavior(ITextRenderer renderer, IJQuer super(renderer, template); } + /** + * Gets the {@link IElementSelectionStrategy}. Index-based selection is used by default. + * + * @return the selection strategy + */ + protected IElementSelectionStrategy getElementSelectionStrategy() + { + return IndexBasedElementSelectionStrategy.get(); + } + @Override protected String getResponse(IRequestParameters parameters) { @@ -61,7 +71,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 */ + String identifier = this.getElementSelectionStrategy().getIdentifier(choice, index); + object.put("id", identifier != null ? identifier : 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/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IAutoCompleteListener.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IAutoCompleteListener.java index 4c091fc58f..d7985042d8 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IAutoCompleteListener.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IAutoCompleteListener.java @@ -16,8 +16,12 @@ */ package org.wicketstuff.jquery.ui.form.autocomplete; +import java.util.List; + import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.util.io.IClusterable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Event listener shared by the {@link AutoCompleteTextField} widget and the {@link AutoCompleteBehavior} @@ -25,13 +29,40 @@ * @author Sebastien Briquet - sebfz1 * */ -public interface IAutoCompleteListener extends IClusterable +public interface IAutoCompleteListener extends IClusterable { + Logger LOG = LoggerFactory.getLogger(IAutoCompleteListener.class); + + /** + * Gets the {@link IElementSelectionStrategy} used to identify a selected choice and to resolve it from the cached choice list. + * + * @return the {@link IElementSelectionStrategy} + */ + IElementSelectionStrategy getElementSelectionStrategy(); + /** * Triggered when a selection has been made * * @param target the {@link AjaxRequestTarget} - * @param index the index of the selected item + * @param choice the selected choice + */ + void onSelect(AjaxRequestTarget target, T choice); + + /** + * Triggered when a selection has been made, using the identifier posted by the client. + *

+ * The default implementation resolves the choice from {@code choiceList} via + * {@link #getElementSelectionStrategy()} and {@link IElementSelectionStrategy#findChoice(List, String)}, + * then delegates to {@link #onSelect(AjaxRequestTarget, Object)}. + * + * @param target the {@link AjaxRequestTarget} + * @param choiceList the cached list of choices matching the last query + * @param identifier the identifier of the selected item (JSON {@code id}; list index by default, or a business id) */ - void onSelect(AjaxRequestTarget target, int index); + default void onSelect(AjaxRequestTarget target, List choiceList, String identifier) + { + + T choice = getElementSelectionStrategy().findChoice(choiceList, identifier); + onSelect(target, choice); + } } diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IElementSelectionStrategy.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IElementSelectionStrategy.java new file mode 100644 index 0000000000..7a1426d634 --- /dev/null +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IElementSelectionStrategy.java @@ -0,0 +1,55 @@ +/* + * 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 org.wicketstuff.jquery.ui.form.autocomplete; + +import java.io.Serializable; +import java.util.List; + +import org.apache.wicket.util.io.IClusterable; + +/** + * Strategy used to identify a selected auto-complete choice and to resolve it from the cached choice list. + *

+ * The identifier is written to the JSON {@code id} field and posted back when the user selects an item. + * {@link IndexBasedElementSelectionStrategy} is the default implementation. + *

+ * Implementations must be {@link Serializable}: the strategy is stored on a Wicket component and is + * serialized with the page/session. + * + * @param the choice type + * @author reiern70 + */ +public interface IElementSelectionStrategy extends IClusterable, Serializable +{ + /** + * Gets the identifier that uniquely represents the given choice in the current result set. + * + * @param choice the choice + * @param index the index of the choice in the current result list + * @return the identifier posted to the client (JSON {@code id}) + */ + String getIdentifier(T choice, int index); + + /** + * Resolves the selected choice from the cached list using the identifier posted by the client. + * + * @param choices the cached list of choices + * @param identifier the identifier posted by the client + * @return the matching choice, or {@code null} if none matches + */ + T findChoice(List choices, String identifier); +} diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IdBasedElementSelectionStrategy.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IdBasedElementSelectionStrategy.java new file mode 100644 index 0000000000..e585f30f73 --- /dev/null +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IdBasedElementSelectionStrategy.java @@ -0,0 +1,68 @@ +/* + * 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 org.wicketstuff.jquery.ui.form.autocomplete; + +import java.util.List; +import java.util.Objects; + +/** + * {@link IElementSelectionStrategy} that identifies choices by an id value. + * Subclasses implement {@link #getValue(Object, int)} to extract that id. + * + * @param the choice type + * @author reiern70 + */ +public abstract class IdBasedElementSelectionStrategy implements IElementSelectionStrategy +{ + private static final long serialVersionUID = 1L; + + /** + * Gets the id value that uniquely represents the given choice. + * + * @param choice the choice + * @param index the index of the choice in the current result list + * @return the id value, or {@code null} if none + */ + public abstract String getValue(T choice, int index); + + @Override + public String getIdentifier(T choice, int index) + { + return this.getValue(choice, index); + } + + @Override + public T findChoice(List choices, String identifier) + { + if (choices == null || identifier == null) + { + return null; + } + + for (int index = 0; index < choices.size(); ++index) + { + T choice = choices.get(index); + + if (Objects.equals(identifier, this.getValue(choice, index))) + { + return choice; + } + } + + return null; + } +} diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IndexBasedElementSelectionStrategy.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IndexBasedElementSelectionStrategy.java new file mode 100644 index 0000000000..3d6d055340 --- /dev/null +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IndexBasedElementSelectionStrategy.java @@ -0,0 +1,86 @@ +/* + * 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 org.wicketstuff.jquery.ui.form.autocomplete; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * {@link IElementSelectionStrategy} that identifies choices by their index in the current result list. + * This is the default strategy used by {@link AbstractAutoCompleteTextField}. + * + * @param the choice type + * @author reiern70 + */ +public class IndexBasedElementSelectionStrategy implements IElementSelectionStrategy +{ + private static final long serialVersionUID = 1L; + private static final Logger logger = LoggerFactory.getLogger(IndexBasedElementSelectionStrategy.class); + + private static final IndexBasedElementSelectionStrategy INSTANCE = new IndexBasedElementSelectionStrategy<>(); + + /** + * Gets a shared instance of this strategy. + * + * @param the choice type + * @return the shared strategy + */ + @SuppressWarnings("unchecked") + public static IndexBasedElementSelectionStrategy get() + { + return (IndexBasedElementSelectionStrategy) INSTANCE; + } + + private Object readResolve() + { + return INSTANCE; + } + + @Override + public String getIdentifier(T choice, int index) + { + return Integer.toString(index); + } + + @Override + public T findChoice(List choices, String identifier) + { + if (choices == null || identifier == null) + { + return null; + } + + try + { + int index = Integer.parseInt(identifier); + + if (-1 < index && index < choices.size()) + { + return choices.get(index); + } + } + catch (NumberFormatException e) + { + logger.warn("Failed to parse identifier '{}' as integer for choices lookup.", identifier, e); + return null; + } + + return null; + } +} diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/PropertyBasedElementSelectionStrategy.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/PropertyBasedElementSelectionStrategy.java new file mode 100644 index 0000000000..63ee541793 --- /dev/null +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/PropertyBasedElementSelectionStrategy.java @@ -0,0 +1,56 @@ +/* + * 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 org.wicketstuff.jquery.ui.form.autocomplete; + +import org.apache.wicket.core.util.lang.PropertyResolver; +import org.apache.wicket.util.lang.Args; + +/** + * {@link IdBasedElementSelectionStrategy} that resolves the id from a bean property expression. + * + * @param the choice type + * @author reiern70 + */ +public class PropertyBasedElementSelectionStrategy extends IdBasedElementSelectionStrategy +{ + private static final long serialVersionUID = 1L; + + private final String idExpression; + + /** + * Constructor + * + * @param idExpression the property expression resolved on each choice (e.g. {@code "id"}) + */ + public PropertyBasedElementSelectionStrategy(String idExpression) + { + this.idExpression = Args.notEmpty(idExpression, "idExpression"); + } + + @Override + public String getValue(T choice, int index) + { + if (choice == null) + { + return null; + } + + Object value = PropertyResolver.getValue(this.idExpression, choice); + + return value != null ? value.toString() : null; + } +} From 8ce23457a83b6076aeab7d1bb044d16bc7d05a84 Mon Sep 17 00:00:00 2001 From: reiern70 Date: Fri, 28 Aug 2026 16:03:02 -0500 Subject: [PATCH 2/7] make selection machinery pluggable so that for instance we can use an ID based mechanism --- .../AbstractAutoCompleteTextField.java | 44 ++++++++++++++++--- .../autocomplete/AutoCompleteBehavior.java | 25 ++++++++--- .../AutoCompleteChoiceModelBehavior.java | 2 +- .../autocomplete/IAutoCompleteListener.java | 10 ++--- 4 files changed, 62 insertions(+), 19 deletions(-) diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java index 3297fe8835..eb200ac20a 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java @@ -24,6 +24,7 @@ import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.model.IModel; import org.apache.wicket.util.convert.IConverter; +import org.danekja.java.util.function.serializable.SerializableSupplier; import org.wicketstuff.jquery.core.IJQueryWidget; import org.wicketstuff.jquery.core.JQueryBehavior; import org.wicketstuff.jquery.core.renderer.ITextRenderer; @@ -307,9 +308,9 @@ protected void onComponentTag(final ComponentTag tag) } @Override - public void onSelect(AjaxRequestTarget target, T choice) { + public void onSelect(AjaxRequestTarget target, T choice,String identifier) { if (choice != null) { - LOG.error("Cannot select choice: " + choice); + LOG.error("Cannot select choice with ID: {}", identifier); } this.setModelObject(choice); this.onSelected(target); @@ -329,9 +330,10 @@ protected void onSelected(AjaxRequestTarget target) @Override public JQueryBehavior newWidgetBehavior(String selector) { - return new AutoCompleteBehavior(selector, this, new IModel>() { + return new AutoCompleteBehavior(selector, this, new SerializableSupplier>() { + @Override - public List getObject() { + public List get() { return choices != null ? choices.getObject() : Collections.emptyList(); } }) { // NOSONAR @@ -427,8 +429,8 @@ protected IElementSelectionStrategy newElementSelectionStrategy() */ private AutoCompleteChoiceModelBehavior newChoiceModelBehavior() { - return new AutoCompleteChoiceModelBehavior(this.renderer, this.template) { // NOSONAR - + return new AutoCompleteChoiceModelBehavior(this.renderer, this.template) // NOSONAR + { private static final long serialVersionUID = 1L; private static final String TERM = "term"; @@ -447,4 +449,34 @@ public List getChoices() } }; } + + /** + * Gets the {@link AutoCompleteChoiceModelBehavior} that serves the choices to the widget + * + * @return the {@link AutoCompleteChoiceModelBehavior}, or {@code null} if the component has not been initialized yet + */ + public final AutoCompleteChoiceModelBehavior getChoiceModelBehavior() + { + return this.choiceModelBehavior; + } + + /** + * Gets the cached choices of the last query, used to resolve the user selected object + * + * @return the {@link IModel} of choices, or {@code null} if no query has been performed yet + */ + public final IModel> getChoices() + { + return this.choices; + } + + /** + * Gets the {@link JQueryAbstractTemplateBehavior} supplied by {@link #newTemplate()} + * + * @return the {@link JQueryAbstractTemplateBehavior}, or {@code null} if there is no template + */ + public final JQueryAbstractTemplateBehavior getTemplateBehavior() + { + return this.templateBehavior; + } } diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteBehavior.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteBehavior.java index 1534c982ec..94570865f6 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteBehavior.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteBehavior.java @@ -23,6 +23,7 @@ import org.apache.wicket.ajax.attributes.CallbackParameter; import org.apache.wicket.model.IModel; import org.apache.wicket.util.lang.Args; +import org.danekja.java.util.function.serializable.SerializableSupplier; import org.wicketstuff.jquery.core.JQueryEvent; import org.wicketstuff.jquery.core.Options; import org.wicketstuff.jquery.core.ajax.IJQueryAjaxAware; @@ -45,7 +46,7 @@ public abstract class AutoCompleteBehavior extends JQueryUIBehavior implement /** the model producing values */ - private final IModel> valuesModel; + private final SerializableSupplier> supplier; private JQueryAjaxBehavior onSelectAjaxBehavior = null; @@ -55,9 +56,9 @@ public abstract class AutoCompleteBehavior extends JQueryUIBehavior implement * @param selector the HTML selector (ie: "#myId") * @param listener the {@link IAutoCompleteListener} */ - public AutoCompleteBehavior(String selector, IAutoCompleteListener listener, IModel> valuesModel) + public AutoCompleteBehavior(String selector, IAutoCompleteListener listener, SerializableSupplier> supplier) { - this(selector, new Options(), listener, valuesModel); + this(selector, new Options(), listener, supplier); } /** @@ -67,12 +68,12 @@ public AutoCompleteBehavior(String selector, IAutoCompleteListener listener, * @param options the {@link Options} * @param listener the {@link IAutoCompleteListener} */ - public AutoCompleteBehavior(String selector, Options options, IAutoCompleteListener listener, IModel> valuesModel) + public AutoCompleteBehavior(String selector, Options options, IAutoCompleteListener listener, SerializableSupplier> supplier) { super(selector, METHOD, options); this.listener = Args.notNull(listener, "listener"); - this.valuesModel = valuesModel; + this.supplier = supplier; } // Methods // @@ -118,7 +119,7 @@ public void onAjax(AjaxRequestTarget target, JQueryEvent event) { if (event instanceof SelectEvent selectEvent) { - this.listener.onSelect(target, listener.getElementSelectionStrategy().findChoice(valuesModel.getObject(), selectEvent.getIdentifier())); + this.listener.onSelect(target, listener.getElementSelectionStrategy().findChoice(supplier.get(), selectEvent.getIdentifier()), selectEvent.getIdentifier()); } } @@ -135,7 +136,7 @@ protected JQueryAjaxBehavior newOnSelectAjaxBehavior(IJQueryAjaxAware source) return new OnSelectAjaxBehavior(source); } - // Ajax classes // + // Ajax classes //, /** * Provides a {@link JQueryAjaxBehavior} that aims to be wired to the 'select' event @@ -188,4 +189,14 @@ public String getIdentifier() return this.identifier; } } + + /** + * Gets the {@link JQueryAjaxBehavior} wired to the 'select' event + * + * @return the {@code OnSelectAjaxBehavior}, or {@code null} if the behavior has not been bound yet + */ + public final JQueryAjaxBehavior getOnSelectAjaxBehavior() + { + return this.onSelectAjaxBehavior; + } } diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteChoiceModelBehavior.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteChoiceModelBehavior.java index f849e5106d..3953a6fc69 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteChoiceModelBehavior.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AutoCompleteChoiceModelBehavior.java @@ -33,7 +33,7 @@ * @param the model object type * @author Sebastien Briquet - sebfz1 */ -abstract class AutoCompleteChoiceModelBehavior extends ChoiceModelBehavior +public abstract class AutoCompleteChoiceModelBehavior extends ChoiceModelBehavior { private static final long serialVersionUID = 1L; diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IAutoCompleteListener.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IAutoCompleteListener.java index d7985042d8..50cebbd539 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IAutoCompleteListener.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/IAutoCompleteListener.java @@ -44,16 +44,17 @@ public interface IAutoCompleteListener extends IClusterable * Triggered when a selection has been made * * @param target the {@link AjaxRequestTarget} - * @param choice the selected choice + * @param choice the selected choice, or {@code null} if the identifier could not be resolved + * @param identifier the identifier of the selected item (JSON {@code id}; list index by default, or a business id) */ - void onSelect(AjaxRequestTarget target, T choice); + void onSelect(AjaxRequestTarget target, T choice, String identifier); /** * Triggered when a selection has been made, using the identifier posted by the client. *

* The default implementation resolves the choice from {@code choiceList} via * {@link #getElementSelectionStrategy()} and {@link IElementSelectionStrategy#findChoice(List, String)}, - * then delegates to {@link #onSelect(AjaxRequestTarget, Object)}. + * then delegates to {@link #onSelect(AjaxRequestTarget, Object, String)}. * * @param target the {@link AjaxRequestTarget} * @param choiceList the cached list of choices matching the last query @@ -61,8 +62,7 @@ public interface IAutoCompleteListener extends IClusterable */ default void onSelect(AjaxRequestTarget target, List choiceList, String identifier) { - T choice = getElementSelectionStrategy().findChoice(choiceList, identifier); - onSelect(target, choice); + onSelect(target, choice, identifier); } } From 4242fc817e8705cae9e5fe8b3b7b8b5018c7d4d3 Mon Sep 17 00:00:00 2001 From: reiern70 Date: Wed, 2 Sep 2026 13:56:54 -0500 Subject: [PATCH 3/7] make selection machinery pluggable so that for instance we can use an ID based mechanism --- .../AbstractAutoCompleteTextField.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java index eb200ac20a..6622009b6b 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java @@ -308,12 +308,23 @@ protected void onComponentTag(final ComponentTag tag) } @Override - public void onSelect(AjaxRequestTarget target, T choice,String identifier) { - if (choice != null) { - LOG.error("Cannot select choice with ID: {}", identifier); - } + public final void onSelect(AjaxRequestTarget target, T choice, String identifier) { this.setModelObject(choice); - this.onSelected(target); + if (choice == null) { + LOG.debug("Cannot select choice with ID: {}", identifier); + onSelectionFailed(target); + } else { + this.onSelected(target); + } + } + + /** + * Triggered when it was not possible to find an element to select + * + * @param target the {@link AjaxRequestTarget} + */ + protected void onSelectionFailed(AjaxRequestTarget target) + { } /** From 8db154b71347cbaa6091801a3a67655fb58ca884 Mon Sep 17 00:00:00 2001 From: reiern70 Date: Wed, 2 Sep 2026 13:57:28 -0500 Subject: [PATCH 4/7] code style --- .../autocomplete/AbstractAutoCompleteTextField.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java index 6622009b6b..efa328ce46 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java @@ -308,12 +308,16 @@ protected void onComponentTag(final ComponentTag tag) } @Override - public final void onSelect(AjaxRequestTarget target, T choice, String identifier) { + public final void onSelect(AjaxRequestTarget target, T choice, String identifier) + { this.setModelObject(choice); - if (choice == null) { + if (choice == null) + { LOG.debug("Cannot select choice with ID: {}", identifier); onSelectionFailed(target); - } else { + } + else + { this.onSelected(target); } } From f7b260e404a2e4cdff1a1dd8e67d1731e8ef8289 Mon Sep 17 00:00:00 2001 From: reiern70 Date: Wed, 2 Sep 2026 13:58:42 -0500 Subject: [PATCH 5/7] code style --- .../ui/form/autocomplete/AbstractAutoCompleteTextField.java | 1 - 1 file changed, 1 deletion(-) diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java index efa328ce46..aeca8eba9c 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java @@ -148,7 +148,6 @@ public AbstractAutoCompleteTextField(String id, IModel model, ITextRenderer Date: Wed, 2 Sep 2026 14:55:15 -0500 Subject: [PATCH 6/7] code style --- .../ui/form/autocomplete/AbstractAutoCompleteTextField.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java index aeca8eba9c..d07a9bfe3f 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui/src/main/java/org/wicketstuff/jquery/ui/form/autocomplete/AbstractAutoCompleteTextField.java @@ -313,7 +313,7 @@ public final void onSelect(AjaxRequestTarget target, T choice, String identifier if (choice == null) { LOG.debug("Cannot select choice with ID: {}", identifier); - onSelectionFailed(target); + onSelectionFailed(target, identifier); } else { @@ -325,8 +325,9 @@ public final void onSelect(AjaxRequestTarget target, T choice, String identifier * Triggered when it was not possible to find an element to select * * @param target the {@link AjaxRequestTarget} + * @param identifier THe id of the Choice that could not be retrieved. */ - protected void onSelectionFailed(AjaxRequestTarget target) + protected void onSelectionFailed(AjaxRequestTarget target, String identifier) { } From c6cc316fb912ad392bf74377418dfc0edbfd2554 Mon Sep 17 00:00:00 2001 From: reiern70 Date: Wed, 2 Sep 2026 15:59:20 -0500 Subject: [PATCH 7/7] add example --- .../jquery/ui/samples/SiteMapPage.java | 2 + .../AbstractAutoCompletePage.java | 3 +- .../autocomplete/IdBasedAutoCompletePage.css | 17 ++++ .../autocomplete/IdBasedAutoCompletePage.html | 32 +++++++ .../autocomplete/IdBasedAutoCompletePage.java | 88 +++++++++++++++++++ .../ui/samples/SampleApplication.properties | 1 + 6 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/java/org/wicketstuff/jquery/ui/samples/jqueryui/autocomplete/IdBasedAutoCompletePage.css create mode 100644 wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/java/org/wicketstuff/jquery/ui/samples/jqueryui/autocomplete/IdBasedAutoCompletePage.html create mode 100644 wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/java/org/wicketstuff/jquery/ui/samples/jqueryui/autocomplete/IdBasedAutoCompletePage.java diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/java/org/wicketstuff/jquery/ui/samples/SiteMapPage.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/java/org/wicketstuff/jquery/ui/samples/SiteMapPage.java index dbbd3e28bf..112691777f 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/java/org/wicketstuff/jquery/ui/samples/SiteMapPage.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/java/org/wicketstuff/jquery/ui/samples/SiteMapPage.java @@ -20,6 +20,7 @@ import org.wicketstuff.jquery.ui.samples.jqueryui.autocomplete.ConverterAutoCompletePage; import org.wicketstuff.jquery.ui.samples.jqueryui.autocomplete.CustomAutoCompletePage; import org.wicketstuff.jquery.ui.samples.jqueryui.autocomplete.DefaultAutoCompletePage; +import org.wicketstuff.jquery.ui.samples.jqueryui.autocomplete.IdBasedAutoCompletePage; import org.wicketstuff.jquery.ui.samples.jqueryui.autocomplete.RendererAutoCompletePage; import org.wicketstuff.jquery.ui.samples.jqueryui.autocomplete.TemplateAutoCompletePage; import org.wicketstuff.jquery.ui.samples.jqueryui.button.AjaxButtonPage; @@ -191,6 +192,7 @@ public class SiteMapPage extends WebPage ContextMenuPage.class, ConverterAutoCompletePage.class, CustomAutoCompletePage.class, + IdBasedAutoCompletePage.class, CustomCalendarPage.class, CustomCalendar6Page.class, CustomComboBoxPage.class, diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/java/org/wicketstuff/jquery/ui/samples/jqueryui/autocomplete/AbstractAutoCompletePage.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/java/org/wicketstuff/jquery/ui/samples/jqueryui/autocomplete/AbstractAutoCompletePage.java index 38ee8c58d3..304d421086 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/java/org/wicketstuff/jquery/ui/samples/jqueryui/autocomplete/AbstractAutoCompletePage.java +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/java/org/wicketstuff/jquery/ui/samples/jqueryui/autocomplete/AbstractAutoCompletePage.java @@ -20,7 +20,8 @@ protected List getDemoLinks() new DemoLink(CustomAutoCompletePage.class, "Auto-complete: custom beans"), // lf new DemoLink(TemplateAutoCompletePage.class, "Auto-complete: custom template"), // lf new DemoLink(RendererAutoCompletePage.class, "Auto-complete: custom renderer"), // lf - new DemoLink(ConverterAutoCompletePage.class, "Auto-complete: form submit") // lf + new DemoLink(ConverterAutoCompletePage.class, "Auto-complete: form submit"), // lf + new DemoLink(IdBasedAutoCompletePage.class, "Auto-complete: id based selection") // lf ); } } diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/java/org/wicketstuff/jquery/ui/samples/jqueryui/autocomplete/IdBasedAutoCompletePage.css b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/java/org/wicketstuff/jquery/ui/samples/jqueryui/autocomplete/IdBasedAutoCompletePage.css new file mode 100644 index 0000000000..e7323a51d7 --- /dev/null +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/java/org/wicketstuff/jquery/ui/samples/jqueryui/autocomplete/IdBasedAutoCompletePage.css @@ -0,0 +1,17 @@ +/* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */ +table { + width: 400px; + border-spacing: 0; + border-collapse: collapse; +} + +th, td { + padding: 5px; +} + +.ui-autocomplete { + max-height: 200px; + overflow-y: auto; + overflow-x: hidden; + padding-right: 20px; +} diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/java/org/wicketstuff/jquery/ui/samples/jqueryui/autocomplete/IdBasedAutoCompletePage.html b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/java/org/wicketstuff/jquery/ui/samples/jqueryui/autocomplete/IdBasedAutoCompletePage.html new file mode 100644 index 0000000000..fb0542c08f --- /dev/null +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/java/org/wicketstuff/jquery/ui/samples/jqueryui/autocomplete/IdBasedAutoCompletePage.html @@ -0,0 +1,32 @@ + + + + + + Wicket jQuery UI: auto-complete (custom bean) + + + + +

+
+ + + + + +
+
Select a metal genre: (containing any char)
+
+ +
+ +
+
+
+
+ + + diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/java/org/wicketstuff/jquery/ui/samples/jqueryui/autocomplete/IdBasedAutoCompletePage.java b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/java/org/wicketstuff/jquery/ui/samples/jqueryui/autocomplete/IdBasedAutoCompletePage.java new file mode 100644 index 0000000000..bad3f8f8b9 --- /dev/null +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/java/org/wicketstuff/jquery/ui/samples/jqueryui/autocomplete/IdBasedAutoCompletePage.java @@ -0,0 +1,88 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.wicketstuff.jquery.ui.samples.jqueryui.autocomplete; + +import java.util.List; + +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.markup.head.IHeaderResponse; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.markup.html.image.ContextImage; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.LoadableDetachableModel; +import org.apache.wicket.model.Model; +import org.apache.wicket.model.PropertyModel; +import org.wicketstuff.jquery.core.resource.StyleSheetPackageHeaderItem; +import org.wicketstuff.jquery.core.utils.ListUtils; +import org.wicketstuff.jquery.ui.form.autocomplete.AbstractAutoCompleteTextField; +import org.wicketstuff.jquery.ui.form.autocomplete.AutoCompleteTextField; +import org.wicketstuff.jquery.ui.form.autocomplete.IElementSelectionStrategy; +import org.wicketstuff.jquery.ui.form.autocomplete.PropertyBasedElementSelectionStrategy; +import org.wicketstuff.jquery.ui.samples.data.bean.Genre; +import org.wicketstuff.jquery.ui.samples.data.dao.GenresDAO; + +public class IdBasedAutoCompletePage extends AbstractAutoCompletePage +{ + private static final long serialVersionUID = 1L; + + public IdBasedAutoCompletePage() + { + // Model // + final IModel model = Model.of(GenresDAO.newGenre()); + + // Form // + final Form form = new Form("form"); + this.add(form); + + // Container for selected genre (name & cover) // + final WebMarkupContainer container = new WebMarkupContainer("container"); + form.add(container.setOutputMarkupId(true)); + + container.add(new ContextImage("cover", new PropertyModel(model, "cover"))); + container.add(new Label("name", new PropertyModel(model, "name"))); + + // Auto-complete // + form.add(new AbstractAutoCompleteTextField("autocomplete", model) { + + private static final long serialVersionUID = 1L; + + @Override + protected IModel> getChoicesModel(String input) + { + return new LoadableDetachableModel<>() + { + @Override + protected List load() + { + return ListUtils.contains(input, GenresDAO.all()); + } + }; + } + + @Override + protected IElementSelectionStrategy newElementSelectionStrategy() + { + return new PropertyBasedElementSelectionStrategy<>("id"); + } + + @Override + protected void onSelected(AjaxRequestTarget target) + { + target.add(container); // the model has already been updated + } + }); + } + + // Methods // + + @Override + public void renderHead(IHeaderResponse response) + { + super.renderHead(response); + + response.render(new StyleSheetPackageHeaderItem(IdBasedAutoCompletePage.class)); + } +} diff --git a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/resources/org/wicketstuff/jquery/ui/samples/SampleApplication.properties b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/resources/org/wicketstuff/jquery/ui/samples/SampleApplication.properties index cdc22247c4..14fdae69cf 100644 --- a/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/resources/org/wicketstuff/jquery/ui/samples/SampleApplication.properties +++ b/wicketstuff-jquery-ui-parent/wicketstuff-jquery-ui-samples/src/main/resources/org/wicketstuff/jquery/ui/samples/SampleApplication.properties @@ -6,6 +6,7 @@ AccordionPanelPage.title=AccordionPanel # Auto-complete DefaultAutoCompletePage.title=Auto-complete CustomAutoCompletePage.title=Auto-complete: custom beans +IdBasedAutoCompletePage.title=Auto-complete: ID based selection TemplateAutoCompletePage.title=Auto-complete: custom template RendererAutoCompletePage.title=Auto-complete: custom renderer ConverterAutoCompletePage.title=Auto-complete: form submit