001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.extensions.ajax.markup.html.autocomplete; 018 019import java.util.Iterator; 020 021import org.apache.wicket.ConverterLocator; 022import org.apache.wicket.ajax.attributes.AjaxRequestAttributes; 023import org.apache.wicket.behavior.Behavior; 024import org.apache.wicket.markup.ComponentTag; 025import org.apache.wicket.markup.html.form.TextField; 026import org.apache.wicket.model.IModel; 027import org.apache.wicket.util.convert.IConverter; 028 029/** 030 * An implementation of a textfield with the autoassist ajax behavior {@link AutoCompleteBehavior}. 031 * <p> 032 * An {@link IAutoCompleteRenderer} is used for rendering of choices. To convert input back into a 033 * non-String type you will have to provide a custom {@link IConverter}, either by overriding 034 * {@link #getConverter(Class)} or by setting a suitable {@link IConverter} on the application's 035 * {@link ConverterLocator}. 036 * <p> 037 * Note that you must add your own CSS to make the suggestion display properly, see 038 * {@link DefaultCssAutoCompleteTextField} for an example. 039 * 040 * @see DefaultCssAutoCompleteTextField 041 * @see AutoCompleteBehavior 042 * @see IAutoCompleteRenderer 043 * 044 * @since 1.2 045 * 046 * @author Igor Vaynberg (ivaynberg) 047 * 048 * @param <T> 049 * The model object type 050 */ 051public abstract class AutoCompleteTextField<T> extends TextField<T> 052{ 053 private static final long serialVersionUID = 1L; 054 055 /** auto complete behavior attached to this textfield */ 056 private AutoCompleteBehavior<T> behavior; 057 058 /** renderer */ 059 private final IAutoCompleteRenderer<T> renderer; 060 061 /** settings */ 062 private final AutoCompleteSettings settings; 063 064 /** 065 * Constructor for the given type with default settings. 066 * 067 * @param id 068 * component id 069 * @param type 070 * model objec type 071 */ 072 public AutoCompleteTextField(final String id, final Class<T> type) 073 { 074 this(id, null, type, new AutoCompleteSettings()); 075 } 076 077 /** 078 * Constructor for the given model and type. 079 * 080 * @param id 081 * component id 082 * @param model 083 * model 084 * @param type 085 * model object type 086 * @param settings 087 * settings for autocomplete 088 */ 089 @SuppressWarnings("unchecked") 090 public AutoCompleteTextField(final String id, final IModel<T> model, final Class<T> type, 091 final AutoCompleteSettings settings) 092 { 093 this(id, model, type, StringAutoCompleteRenderer.INSTANCE, settings); 094 } 095 096 /** 097 * Constructor for given model. 098 * 099 * @param id 100 * component id 101 * @param model 102 * model 103 * @param settings 104 * settings for autocomplete 105 */ 106 public AutoCompleteTextField(final String id, final IModel<T> model, 107 final AutoCompleteSettings settings) 108 { 109 this(id, model, null, settings); 110 } 111 112 /** 113 * Constructor for the given model. 114 * 115 * @param id 116 * component id 117 * @param model 118 * model 119 */ 120 public AutoCompleteTextField(final String id, final IModel<T> model) 121 { 122 this(id, model, null, new AutoCompleteSettings()); 123 } 124 125 /** 126 * Constructor. 127 * 128 * @param id 129 * component id 130 * @param settings 131 * settings for autocomplete 132 */ 133 public AutoCompleteTextField(final String id, final AutoCompleteSettings settings) 134 { 135 this(id, null, settings); 136 } 137 138 /** 139 * Constructor. 140 * 141 * @param id 142 * component id 143 */ 144 public AutoCompleteTextField(final String id) 145 { 146 this(id, null, new AutoCompleteSettings()); 147 } 148 149 /** 150 * Constructor using the given renderer. 151 * 152 * @param id 153 * component id 154 * @param renderer 155 * renderer for autocomplete 156 */ 157 public AutoCompleteTextField(final String id, final IAutoCompleteRenderer<T> renderer) 158 { 159 this(id, (IModel<T>)null, renderer); 160 } 161 162 /** 163 * Constructor for the given type using the given renderer 164 * 165 * @param id 166 * component id 167 * @param type 168 * model object type 169 * @param renderer 170 * renderer for autocomplete 171 */ 172 public AutoCompleteTextField(final String id, final Class<T> type, 173 final IAutoCompleteRenderer<T> renderer) 174 { 175 this(id, null, type, renderer, new AutoCompleteSettings()); 176 } 177 178 /** 179 * Constructor for the given model using the given renderer. 180 * 181 * @param id 182 * component id 183 * @param model 184 * model 185 * @param renderer 186 * renderer for autocomplete 187 */ 188 public AutoCompleteTextField(final String id, final IModel<T> model, 189 final IAutoCompleteRenderer<T> renderer) 190 { 191 this(id, model, null, renderer, new AutoCompleteSettings()); 192 } 193 194 /** 195 * Constructor for the given model using the given renderer. 196 * 197 * @param id 198 * component id 199 * @param model 200 * model 201 * @param type 202 * model object type 203 * @param renderer 204 * renderer for autocomplete 205 * @param settings 206 * settings for autocomplete 207 */ 208 public AutoCompleteTextField(final String id, final IModel<T> model, final Class<T> type, 209 final IAutoCompleteRenderer<T> renderer, final AutoCompleteSettings settings) 210 { 211 super(id, model, type); 212 this.renderer = renderer; 213 this.settings = settings; 214 } 215 216 /** 217 * Factory method for autocomplete behavior that will be added to this textfield 218 * 219 * @param renderer 220 * auto complete renderer 221 * @param settings 222 * auto complete settings 223 * @return auto complete behavior 224 */ 225 protected AutoCompleteBehavior<T> newAutoCompleteBehavior( 226 final IAutoCompleteRenderer<T> renderer, final AutoCompleteSettings settings) 227 { 228 return new AutoCompleteBehavior<>(renderer, settings) 229 { 230 private static final long serialVersionUID = 1L; 231 232 @Override 233 protected Iterator<T> getChoices(final String input) 234 { 235 return AutoCompleteTextField.this.getChoices(input); 236 } 237 238 @Override 239 protected void updateAjaxAttributes(AjaxRequestAttributes attributes) 240 { 241 super.updateAjaxAttributes(attributes); 242 243 AutoCompleteTextField.this.updateAjaxAttributes(attributes); 244 } 245 }; 246 } 247 248 protected void updateAjaxAttributes(AjaxRequestAttributes attributes) 249 { 250 } 251 252 /** 253 * Initializes the {@link AutoCompleteBehavior} if it is not already there. 254 */ 255 @Override 256 protected void onInitialize() 257 { 258 super.onInitialize(); 259 260 initializeAutoCompleteBehavior(); 261 } 262 263 /** 264 * The {@link AutoCompleteBehavior} is added lazily instead from the constructor to support an 265 * overridable factory method. 266 * 267 * @see #onInitialize() 268 * @see #add(Behavior...) 269 * @see #newAutoCompleteBehavior(IAutoCompleteRenderer, AutoCompleteSettings) 270 */ 271 private void initializeAutoCompleteBehavior() 272 { 273 // add auto complete behavior to this component if its not already there 274 if (behavior == null) 275 { 276 super.add(behavior = newAutoCompleteBehavior(renderer, settings)); 277 } 278 } 279 280 @Override 281 protected void onComponentTag(final ComponentTag tag) 282 { 283 super.onComponentTag(tag); 284 285 // disable browser's autocomplete 286 tag.put("autocomplete", "off"); 287 } 288 289 /** 290 * Callback method that should return an iterator over all possible assist choice objects. These 291 * objects will be passed to the renderer to generate output. Usually it is enough to return an 292 * iterator over strings. 293 * 294 * @see AutoCompleteBehavior#getChoices(String) 295 * 296 * @param input 297 * current input 298 * @return iterator over all possible choice objects 299 */ 300 protected abstract Iterator<T> getChoices(String input); 301 302 /** 303 * @return The {@link IAutoCompleteRenderer} used to generate html output for the 304 * {@link AutoCompleteBehavior}. 305 */ 306 public final IAutoCompleteRenderer<T> getChoiceRenderer() 307 { 308 return renderer; 309 } 310 311 @Override 312 protected void onDetach() 313 { 314 renderer.detach(); 315 316 super.onDetach(); 317 } 318}