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 }