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(&quot;left&quot;, new PropertyModel(this, &quot;lhs&quot;), Integer.class));
076 *              add(right = new TextField(&quot;right&quot;, new PropertyModel(this, &quot;rhs&quot;), Integer.class));
077 *              left.setRequired(true);
078 *              right.setRequired(true);
079 *      }
080 * }
081 * </pre>
082 * 
083 * With this markup:
084 * 
085 * <pre>
086 *   &lt;wicket:panel&gt;
087 *     &lt;input type=&quot;text&quot; wicket:id=&quot;left&quot; size=&quot;2&quot; /&gt; * &lt;input type=&quot;text&quot; wicket:id=&quot;right&quot; size=&quot;2&quot; /&gt;
088 *   &lt;/wicket:panel&gt;
089 * </pre>
090 * 
091 * Which could be used, for example as:
092 * 
093 * <pre>
094 *   add(new Multiply(&quot;multiply&quot;), new PropertyModel(m, &quot;multiply&quot;)));
095 *   add(new Label(&quot;multiplyLabel&quot;, new PropertyModel(m, &quot;multiply&quot;)));
096 * </pre>
097 * 
098 * and:
099 * 
100 * <pre>
101 *   &lt;span wicket:id=&quot;multiply&quot;&gt;[multiply]&lt;/span&gt;
102 *   = &lt;span wicket:id=&quot;multiplyLabel&quot;&gt;[result]&lt;/span&gt;
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}