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