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.markup.html.form; 018 019import org.apache.wicket.IQueueRegion; 020import org.apache.wicket.markup.ComponentTag; 021import org.apache.wicket.markup.html.panel.IMarkupSourcingStrategy; 022import org.apache.wicket.markup.html.panel.PanelMarkupSourcingStrategy; 023import org.apache.wicket.model.IModel; 024import org.apache.wicket.util.visit.IVisitor; 025 026/** 027 * Panel (has it's own markup, defined between <wicket:panel> tags), that can act as a form 028 * component. It typically wouldn't receive any input yourself, and often you can get by with 029 * nesting form components in panels proper. However, using this panel can help you with building 030 * components act to the outside world as one component, but internally uses separate components. 031 * This component would then use these nested components to handle it's internal state, and would 032 * use that internal state to get to one model object. 033 * <p> 034 * It is recommended that you override {@link #convertInput()} and let it set the value that 035 * represents the compound value of the nested components. Often, this goes hand-in-hand with 036 * overriding {@link #onBeforeRender()}, where you would analyze the model value, break it up and 037 * distribute the appropriate values over the child components. 038 * </p> 039 * 040 * <p> 041 * Here is a simple example of a panel with two components that multiplies and sets that as the 042 * master model object. Note that for this simple example, setting the model value wouldn't make 043 * sense, as the lhs and rhs cannot be known. 044 * </p> 045 * 046 * <pre> 047 * public class Multiply extends FormComponentPanel 048 * { 049 * private TextField left; 050 * private int lhs = 0; 051 * private int rhs = 0; 052 * private TextField right; 053 * 054 * public Multiply(String id) 055 * { 056 * super(id); 057 * init(); 058 * } 059 * 060 * public Multiply(String id, IModel model) 061 * { 062 * super(id, model); 063 * init(); 064 * } 065 * 066 * protected void convertInput() 067 * { 068 * Integer lhs = (Integer)left.getConvertedInput(); 069 * Integer rhs = (Integer)right.getConvertedInput(); 070 * setConvertedInput(lhs * rhs); 071 * } 072 * 073 * private void init() 074 * { 075 * add(left = new TextField("left", new PropertyModel(this, "lhs"), Integer.class)); 076 * add(right = new TextField("right", new PropertyModel(this, "rhs"), Integer.class)); 077 * left.setRequired(true); 078 * right.setRequired(true); 079 * } 080 * } 081 * </pre> 082 * 083 * With this markup: 084 * 085 * <pre> 086 * <wicket:panel> 087 * <input type="text" wicket:id="left" size="2" /> * <input type="text" wicket:id="right" size="2" /> 088 * </wicket:panel> 089 * </pre> 090 * 091 * Which could be used, for example as: 092 * 093 * <pre> 094 * add(new Multiply("multiply"), new PropertyModel(m, "multiply"))); 095 * add(new Label("multiplyLabel", new PropertyModel(m, "multiply"))); 096 * </pre> 097 * 098 * and: 099 * 100 * <pre> 101 * <span wicket:id="multiply">[multiply]</span> 102 * = <span wicket:id="multiplyLabel">[result]</span> 103 * </pre> 104 * 105 * </p> 106 * 107 * @author eelcohillenius 108 * 109 * @param <T> 110 * The model object type 111 */ 112public abstract class FormComponentPanel<T> extends FormComponent<T> implements IQueueRegion 113{ 114 private static final long serialVersionUID = 1L; 115 116 /** 117 * Constructor. 118 * 119 * @param id 120 * The component id 121 */ 122 public FormComponentPanel(String id) 123 { 124 super(id); 125 } 126 127 /** 128 * Constructor. 129 * 130 * @param id 131 * The component id 132 * @param model 133 * The component model 134 */ 135 public FormComponentPanel(String id, IModel<T> model) 136 { 137 super(id, model); 138 } 139 140 @Override 141 public boolean checkRequired() 142 { 143 return true; 144 } 145 146 @Override 147 protected IMarkupSourcingStrategy newMarkupSourcingStrategy() 148 { 149 return PanelMarkupSourcingStrategy.get(false); 150 } 151 152 @Override 153 protected void onComponentTag(final ComponentTag tag) 154 { 155 super.onComponentTag(tag); 156 157 // remove unapplicable attributes that might have been set by the call to super 158 tag.remove("name"); 159 tag.remove("disabled"); 160 } 161 162 @Override 163 public void clearInput() 164 { 165 super.clearInput(); 166 167 // Visit all the (visible) form components and clear the input on each. 168 visitFormComponentsPostOrder(this, (IVisitor<FormComponent<?>, Void>) (formComponent, visit) -> { 169 if (formComponent != FormComponentPanel.this && formComponent.isVisibleInHierarchy()) 170 { 171 formComponent.clearInput(); 172 } 173 }); 174 } 175}