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; 018 019import java.util.List; 020 021import org.apache.wicket.Component; 022import org.apache.wicket.MarkupContainer; 023import org.apache.wicket.ajax.attributes.AjaxCallListener; 024import org.apache.wicket.ajax.attributes.AjaxRequestAttributes; 025import org.apache.wicket.markup.ComponentTag; 026import org.apache.wicket.markup.MarkupStream; 027import org.apache.wicket.markup.html.WebComponent; 028import org.apache.wicket.markup.html.basic.Label; 029import org.apache.wicket.markup.html.form.DropDownChoice; 030import org.apache.wicket.markup.html.form.FormComponent; 031import org.apache.wicket.markup.html.form.IChoiceRenderer; 032import org.apache.wicket.markup.html.form.TextField; 033import org.apache.wicket.model.IModel; 034import org.apache.wicket.model.Model; 035import org.apache.wicket.util.convert.IConverter; 036import org.apache.wicket.util.string.Strings; 037 038 039/** 040 * An inplace editor much like {@link AjaxEditableLabel}, but instead of a {@link TextField} a 041 * {@link DropDownChoice} is displayed. 042 * 043 * @param <T> 044 * @author Eelco Hillenius 045 */ 046public class AjaxEditableChoiceLabel<T> extends AjaxEditableLabel<T> 047{ 048 private static final long serialVersionUID = 1L; 049 050 /** The list of objects. */ 051 private IModel<? extends List<? extends T>> choices; 052 053 /** The renderer used to generate display/id values for the objects. */ 054 private IChoiceRenderer<? super T> renderer; 055 056 /** 057 * Construct. 058 * 059 * @param id 060 * The component id 061 */ 062 public AjaxEditableChoiceLabel(final String id) 063 { 064 super(id); 065 } 066 067 /** 068 * Construct. 069 * 070 * @param id 071 * The component id 072 * @param model 073 * The model 074 */ 075 public AjaxEditableChoiceLabel(final String id, final IModel<T> model) 076 { 077 super(id, model); 078 } 079 080 /** 081 * Construct. 082 * 083 * @param id 084 * The component id 085 * @param choices 086 * The collection of choices in the dropdown 087 */ 088 public AjaxEditableChoiceLabel(final String id, final List<? extends T> choices) 089 { 090 this(id, null, choices); 091 } 092 093 /** 094 * Construct. 095 * 096 * @param id 097 * The component id 098 * @param model 099 * The model 100 * @param choices 101 * The collection of choices in the dropdown 102 */ 103 public AjaxEditableChoiceLabel(final String id, final IModel<T> model, 104 final IModel<? extends List<? extends T>> choices) 105 { 106 super(id, model); 107 this.choices = choices; 108 } 109 110 /** 111 * Construct. 112 * 113 * @param id 114 * The component id 115 * @param model 116 * The model 117 * @param choices 118 * The collection of choices in the dropdown 119 * @param renderer 120 * The rendering engine 121 */ 122 public AjaxEditableChoiceLabel(final String id, final IModel<T> model, 123 final IModel<? extends List<? extends T>> choices, final IChoiceRenderer<? super T> renderer) 124 { 125 super(id, model); 126 this.choices = choices; 127 this.renderer = renderer; 128 } 129 130 /** 131 * Construct. 132 * 133 * @param id 134 * The component id 135 * @param model 136 * The model 137 * @param choices 138 * The collection of choices in the dropdown 139 */ 140 public AjaxEditableChoiceLabel(final String id, final IModel<T> model, 141 final List<? extends T> choices) 142 { 143 this(id, model, Model.ofList(choices)); 144 } 145 146 /** 147 * Construct. 148 * 149 * @param id 150 * The component id 151 * @param model 152 * The model 153 * @param choices 154 * The collection of choices in the dropdown 155 * @param renderer 156 * The rendering engine 157 */ 158 public AjaxEditableChoiceLabel(final String id, final IModel<T> model, 159 final List<? extends T> choices, final IChoiceRenderer<? super T> renderer) 160 { 161 this(id, model, Model.ofList(choices), renderer); 162 } 163 164 /** 165 * {@inheritDoc} 166 */ 167 @Override 168 protected FormComponent<T> newEditor(final MarkupContainer parent, final String componentId, 169 final IModel<T> model) 170 { 171 IModel<List<? extends T>> choiceModel = new IModel<List<? extends T>>() 172 { 173 private static final long serialVersionUID = 1L; 174 175 @Override 176 public List<? extends T> getObject() 177 { 178 return choices.getObject(); 179 } 180 }; 181 182 DropDownChoice<T> editor = new DropDownChoice<T>(componentId, model, choiceModel, renderer) 183 { 184 private static final long serialVersionUID = 1L; 185 186 @Override 187 protected void onModelChanged() 188 { 189 AjaxEditableChoiceLabel.this.onModelChanged(); 190 } 191 192 @Override 193 protected void onModelChanging() 194 { 195 AjaxEditableChoiceLabel.this.onModelChanging(); 196 } 197 198 }; 199 200 editor.setOutputMarkupId(true); 201 editor.setVisible(false); 202 editor.add(new EditorAjaxBehavior() 203 { 204 private static final long serialVersionUID = 1L; 205 206 @Override 207 protected void updateAjaxAttributes(AjaxRequestAttributes attributes) 208 { 209 super.updateAjaxAttributes(attributes); 210 attributes.setEventNames("change", "blur", "keyup"); 211 212 CharSequence dynamicExtraParameters = "var result = [], " 213 + "kc=Wicket.Event.keyCode(attrs.event)," 214 + "evtType=attrs.event.type;" 215 + "if (evtType === 'blur' || (evtType === 'keyup' && kc===27)) {" 216 + " result.push( { name: 'save', value: false } );" 217 + "}" 218 + "else {" 219 + " result = Wicket.Form.serializeElement(attrs.c);" 220 + " result.push( { name: 'save', value: true } );" 221 + "}" 222 + "return result;"; 223 attributes.getDynamicExtraParameters().add(dynamicExtraParameters); 224 225 CharSequence precondition = "var kc=Wicket.Event.keyCode(attrs.event)," 226 + "evtType=attrs.event.type," 227 + "ret=false;" 228 + "if(evtType==='blur' || evtType==='change' || (evtType==='keyup' && kc===27)) ret = true;" 229 + "return ret;"; 230 AjaxCallListener ajaxCallListener = new AjaxCallListener(); 231 ajaxCallListener.onPrecondition(precondition); 232 attributes.getAjaxCallListeners().add(ajaxCallListener); 233 } 234 }); 235 return editor; 236 } 237 238 /** 239 * {@inheritDoc} 240 */ 241 @Override 242 protected Component newLabel(final MarkupContainer parent, final String componentId, 243 final IModel<T> model) 244 { 245 Label label = new Label(componentId, model) 246 { 247 private static final long serialVersionUID = 1L; 248 249 /** 250 * {@inheritDoc} 251 */ 252 @Override 253 public <C> IConverter<C> getConverter(final Class<C> type) 254 { 255 return AjaxEditableChoiceLabel.this.getConverter(type); 256 } 257 258 /** 259 * {@inheritDoc} 260 */ 261 @SuppressWarnings("unchecked") 262 @Override 263 public void onComponentTagBody(final MarkupStream markupStream, 264 final ComponentTag openTag) 265 { 266 String displayValue = getDefaultModelObjectAsString(); 267 if (renderer != null) 268 { 269 Object displayObject = renderer.getDisplayValue(getModelObject()); 270 Class<?> objectClass = (displayObject == null ? null : displayObject.getClass()); 271 272 if ((objectClass != null) && (objectClass != String.class)) 273 { 274 @SuppressWarnings("rawtypes") 275 final IConverter converter = getConverter(objectClass); 276 displayValue = converter.convertToString(displayObject, getLocale()); 277 } 278 else if (displayObject != null) 279 { 280 displayValue = displayObject.toString(); 281 } 282 } 283 284 if (Strings.isEmpty(displayValue)) 285 { 286 replaceComponentTagBody(markupStream, openTag, defaultNullLabel()); 287 } 288 else 289 { 290 replaceComponentTagBody(markupStream, openTag, displayValue); 291 } 292 } 293 }; 294 label.setOutputMarkupId(true); 295 label.add(new LabelAjaxBehavior(getLabelAjaxEvent())); 296 return label; 297 } 298 299 /** 300 * {@inheritDoc} 301 */ 302 @Override 303 protected void onModelChanged() 304 { 305 super.onModelChanged(); 306 } 307 308 /** 309 * {@inheritDoc} 310 */ 311 @Override 312 protected void onModelChanging() 313 { 314 super.onModelChanging(); 315 } 316 317 /** 318 * {@inheritDoc} 319 */ 320 @Override 321 protected void onDetach() 322 { 323 if (choices != null) 324 { 325 choices.detach(); 326 } 327 328 if (renderer != null) 329 { 330 renderer.detach(); 331 } 332 333 super.onDetach(); 334 } 335}