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