001 // Copyright 2009, 2010, 2011 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.tapestry5.corelib.components;
016
017 import org.apache.tapestry5.*;
018 import org.apache.tapestry5.annotations.Environmental;
019 import org.apache.tapestry5.annotations.Parameter;
020 import org.apache.tapestry5.annotations.SupportsInformalParameters;
021 import org.apache.tapestry5.dom.Element;
022 import org.apache.tapestry5.ioc.annotations.Inject;
023 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
024 import org.apache.tapestry5.services.ComponentDefaultProvider;
025 import org.apache.tapestry5.services.FormSupport;
026 import org.apache.tapestry5.services.Request;
027 import org.apache.tapestry5.services.javascript.JavaScriptSupport;
028
029 /**
030 * Used to record a page property as a value into the form. The value is encoded
031 * when the form is rendered, then decoded after the form is submitted back to
032 * the server, and the "value" parameter updated.
033 * <p/>
034 * The encoding and decoding is done via a {@link org.apache.tapestry5.ValueEncoder},
035 * therefore you must either bind the "encoder" parameter to a ValueEncoder or
036 * use an entity type for the "value" parameter for which Tapestry can provide a
037 * ValueEncoder automatically.
038 *
039 * @tapestrydoc
040 * @since 5.1.0.2
041 */
042 @SupportsInformalParameters
043 public class Hidden implements ClientElement
044 {
045 /**
046 * The value to read (when rendering) or update (when the form is submitted).
047 */
048 @Parameter(required = true, autoconnect = true, principal = true)
049 private Object value;
050
051 /**
052 * Defines how nulls on the server side, or sent from the client side, are treated. The selected strategy may
053 * replace the nulls with some other value. The default strategy leaves nulls alone. Another built-in strategy,
054 * zero, replaces nulls with the value 0.
055 */
056 @Parameter(defaultPrefix = BindingConstants.NULLFIELDSTRATEGY, value = "default")
057 private NullFieldStrategy nulls;
058
059 /**
060 * A ValueEncoder used to convert the server-side object provided by the
061 * "value" parameter into a unique client-side string (typically an ID) and
062 * back. Note: this parameter may be OMITTED if Tapestry is configured to
063 * provide a ValueEncoder automatically for the type of property bound to
064 * the "value" parameter.
065 */
066 @Parameter(required = true)
067 private ValueEncoder encoder;
068
069 private String clientId;
070
071 private String controlName;
072
073 private Element hiddenInputElement;
074
075 @Environmental(false)
076 private FormSupport formSupport;
077
078 @Environmental
079 private JavaScriptSupport jsSupport;
080
081 @Inject
082 private ComponentResources resources;
083
084 @Inject
085 private ComponentDefaultProvider defaultProvider;
086
087 @Inject
088 private Request request;
089
090 ValueEncoder defaultEncoder()
091 {
092 return defaultProvider.defaultValueEncoder("value", resources);
093 }
094
095 static class ProcessSubmission implements ComponentAction<Hidden>
096 {
097 private final String controlName;
098
099 public ProcessSubmission(String controlName)
100 {
101 this.controlName = controlName;
102 }
103
104 public void execute(Hidden component)
105 {
106 component.processSubmission(controlName);
107 }
108 }
109
110 boolean beginRender(MarkupWriter writer)
111 {
112 if (formSupport == null)
113 {
114 throw new RuntimeException("The Hidden component must be enclosed by a Form component.");
115 }
116
117 controlName = formSupport.allocateControlName(resources.getId());
118
119 clientId = null;
120
121 formSupport.store(this, new ProcessSubmission(controlName));
122
123 Object toEncode = value == null ? nulls.replaceToClient() : value;
124
125 String encoded = toEncode == null ? "" : encoder.toClient(toEncode);
126
127 hiddenInputElement = writer.element("input", "type", "hidden", "name", controlName, "value", encoded);
128
129 resources.renderInformalParameters(writer);
130
131 writer.end();
132
133 return false;
134 }
135
136 private void processSubmission(String controlName)
137 {
138 String encoded = request.getParameter(controlName);
139
140 String toDecode = InternalUtils.isBlank(encoded) ? nulls.replaceFromClient() : encoded;
141
142 Object decoded = toDecode == null ? null : encoder.toValue(toDecode);
143
144 value = decoded;
145 }
146
147 public String getClientId()
148 {
149 if (clientId == null)
150 {
151 clientId = jsSupport.allocateClientId(resources);
152 hiddenInputElement.forceAttributes("id", clientId);
153 }
154
155 return clientId;
156 }
157
158 public String getControlName()
159 {
160 return controlName;
161 }
162 }