freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From woon...@apache.org
Subject freemarker git commit: FREEMARKER-55: Adding radiobuttons directive
Date Mon, 23 Jul 2018 22:26:22 GMT
Repository: freemarker
Updated Branches:
  refs/heads/3 d389b6cce -> e1feeb22e


FREEMARKER-55: Adding radiobuttons directive


Project: http://git-wip-us.apache.org/repos/asf/freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/freemarker/commit/e1feeb22
Tree: http://git-wip-us.apache.org/repos/asf/freemarker/tree/e1feeb22
Diff: http://git-wip-us.apache.org/repos/asf/freemarker/diff/e1feeb22

Branch: refs/heads/3
Commit: e1feeb22ed16a32851a07676da367b8e026effcd
Parents: d389b6c
Author: Woonsan Ko <woonsan@apache.org>
Authored: Mon Jul 23 18:26:15 2018 -0400
Committer: Woonsan Ko <woonsan@apache.org>
Committed: Mon Jul 23 18:26:15 2018 -0400

----------------------------------------------------------------------
 FM3-CHANGE-LOG.txt                              |   2 +
 ...ltiCheckedElementTemplateDirectiveModel.java |  21 +++-
 .../form/CheckboxesTemplateDirectiveModel.java  |  55 ---------
 .../RadioButtonsTemplateDirectiveModel.java     |  69 +++++++++++
 .../SpringFormTemplateCallableHashModel.java    |   1 +
 .../RadioButtonsTemplateDirectiveModelTest.java | 113 +++++++++++++++++++
 .../form/radiobuttons-directive-usages.f3ah     |  83 ++++++++++++++
 7 files changed, 287 insertions(+), 57 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/freemarker/blob/e1feeb22/FM3-CHANGE-LOG.txt
----------------------------------------------------------------------
diff --git a/FM3-CHANGE-LOG.txt b/FM3-CHANGE-LOG.txt
index 2ccbb91..14eb2e8 100644
--- a/FM3-CHANGE-LOG.txt
+++ b/FM3-CHANGE-LOG.txt
@@ -557,6 +557,8 @@ models by default like FreemarkerServlet does.
   - <form:options ... /> : Replaced by <@form.options ... /> directive.
   - <form:checkbox ... /> : Replaced by <@form.checkbox ... /> directive.
   - <form:checkboxes ... /> : Replaced by <@form.checkboxes ... /> directive.
+  - <form:radiobutton ... /> : Replaced by <@form.radiobutton ... /> directive.
+  - <form:radiobuttons ... /> : Replaced by <@form.radiobuttons ... /> directive.
 
 Core / Miscellaneous
 ....................

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e1feeb22/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractMultiCheckedElementTemplateDirectiveModel.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractMultiCheckedElementTemplateDirectiveModel.java
b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractMultiCheckedElementTemplateDirectiveModel.java
index 1335ff7..c23a705 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractMultiCheckedElementTemplateDirectiveModel.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractMultiCheckedElementTemplateDirectiveModel.java
@@ -93,6 +93,16 @@ abstract class AbstractMultiCheckedElementTemplateDirectiveModel extends
Abstrac
     }
 
     @Override
+    public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
+        return ARGS_LAYOUT;
+    }
+
+    @Override
+    public boolean isNestedContentSupported() {
+        return false;
+    }
+
+    @Override
     protected void executeInternal(TemplateModel[] args, CallPlace callPlace, Writer out,
Environment env,
             ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext)
             throws TemplateException, IOException {
@@ -203,8 +213,15 @@ abstract class AbstractMultiCheckedElementTemplateDirectiveModel extends
Abstrac
         return autogenerateId(env);
     }
 
-    protected abstract void writeAdditionalDetails(Environment env, TagOutputter tagOut)
-            throws TemplateException, IOException;
+    /**
+     * Optionally extend this to write out more detail such as 'field was present' marker
tags.
+     * @param env environment
+     * @param tagOut TagOutputter instance
+     * @throws TemplateException if template exception occurs
+     * @throws IOException if IO exception occurs
+     */
+    protected void writeAdditionalDetails(Environment env, TagOutputter tagOut) throws TemplateException,
IOException {
+    }
 
     private void writeObjectEntry(final Environment env, final TagOutputter tagOut, String
valueProperty,
             String labelProperty, Object item, int itemIndex) throws TemplateException, IOException
{

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e1feeb22/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/CheckboxesTemplateDirectiveModel.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/CheckboxesTemplateDirectiveModel.java
b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/CheckboxesTemplateDirectiveModel.java
index e846d9f..170c79a 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/CheckboxesTemplateDirectiveModel.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/CheckboxesTemplateDirectiveModel.java
@@ -20,21 +20,13 @@
 package org.apache.freemarker.spring.model.form;
 
 import java.io.IOException;
-import java.io.Writer;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.apache.freemarker.core.CallPlace;
 import org.apache.freemarker.core.Environment;
 import org.apache.freemarker.core.TemplateException;
-import org.apache.freemarker.core.model.ArgumentArrayLayout;
-import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.util.CallableUtils;
-import org.apache.freemarker.core.util.StringToIndexMap;
 import org.springframework.web.bind.WebDataBinder;
-import org.springframework.web.servlet.support.RequestContext;
 
 /**
  * Provides <code>TemplateModel</code> for data-binding-aware multiple HTML '{@code
<input type="checkbox"/>}' elements.
@@ -71,58 +63,11 @@ class CheckboxesTemplateDirectiveModel extends AbstractMultiCheckedElementTempla
 
     public static final String NAME = "checkboxes";
 
-    private static final int NAMED_ARGS_OFFSET = AbstractMultiCheckedElementTemplateDirectiveModel.ARGS_LAYOUT
-            .getPredefinedNamedArgumentsEndIndex();
-
-    private static final int VALUE_PARAM_IDX = NAMED_ARGS_OFFSET;
-    private static final String VALUE_PARAM_NAME = "value";
-
-    private static final int LABEL_PARAM_IDX = NAMED_ARGS_OFFSET + 1;
-    private static final String LABEL_PARAM_NAME = "label";
-
-    protected static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(1,
false,
-            StringToIndexMap.of(
-                    AbstractMultiCheckedElementTemplateDirectiveModel.ARGS_LAYOUT.getPredefinedNamedArgumentsMap(),
-                    new StringToIndexMap.Entry(VALUE_PARAM_NAME, VALUE_PARAM_IDX),
-                    new StringToIndexMap.Entry(LABEL_PARAM_NAME, LABEL_PARAM_IDX)),
-            true);
-
-    private String value;
-    private String label;
-
     protected CheckboxesTemplateDirectiveModel(HttpServletRequest request, HttpServletResponse
response) {
         super(request, response);
     }
 
     @Override
-    public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
-        return ARGS_LAYOUT;
-    }
-
-    @Override
-    public boolean isNestedContentSupported() {
-        return false;
-    }
-
-    @Override
-    protected void executeInternal(TemplateModel[] args, CallPlace callPlace, Writer out,
Environment env,
-            ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext)
-            throws TemplateException, IOException {
-        value = CallableUtils.getOptionalStringArgument(args, VALUE_PARAM_IDX, this);
-        label = CallableUtils.getOptionalStringArgument(args, LABEL_PARAM_IDX, this);
-
-        super.executeInternal(args, callPlace, out, env, objectWrapperAndUnwrapper, requestContext);
-    }
-
-    public String getValue() {
-        return value;
-    }
-
-    public String getLabel() {
-        return label;
-    }
-
-    @Override
     protected void writeAdditionalDetails(Environment env, TagOutputter tagOut) throws TemplateException,
IOException {
         if (!isDisabled()) {
             // Spring Web MVC requires to render a hidden input as a 'field was present'
marker.

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e1feeb22/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/RadioButtonsTemplateDirectiveModel.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/RadioButtonsTemplateDirectiveModel.java
b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/RadioButtonsTemplateDirectiveModel.java
new file mode 100644
index 0000000..26e6ef8
--- /dev/null
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/RadioButtonsTemplateDirectiveModel.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.spring.model.form;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Provides <code>TemplateModel</code> for data-binding-aware multiple HTML '{@code
<input type="radio"/>}' elements.
+ * This tag is provided for completeness if the application relies on a
+ * <code>org.springframework.web.servlet.support.RequestDataValueProcessor</code>.
+ * <P>
+ * This directive supports the following parameters:
+ * <UL>
+ * <LI>
+ *   ... TODO ...
+ * </LI>
+ * </UL>
+ * </P>
+ * <P>
+ * Some valid example(s):
+ * </P>
+ * <PRE>
+ *   &lt;#assign genders = [ "F", "M", "U" ] &gt;
+ *   &lt;@form.radiobuttons "user.gender" items=genders /&gt;
+ * </PRE>
+ * Or,
+ * <PRE>
+ *   &lt;#assign genderHash = { "F": "Female", "M": "Male", "U": "Unspecified" } &gt;
+ *   &lt;@form.radiobuttons "user.gender" items=genderHash /&gt;
+ * </PRE>
+ * <P>
+ * <EM>Note:</EM> Unlike Spring Framework's <code>&lt;form:button /&gt;</code>
JSP Tag Library, this directive
+ * does not support <code>htmlEscape</code> parameter. It always renders HTML's
without escaping
+ * because it is much easier to control escaping in FreeMarker Template expressions.
+ * </P>
+ */
+
+class RadioButtonsTemplateDirectiveModel extends AbstractMultiCheckedElementTemplateDirectiveModel
{
+
+    public static final String NAME = "radiobuttons";
+
+    protected RadioButtonsTemplateDirectiveModel(HttpServletRequest request, HttpServletResponse
response) {
+        super(request, response);
+    }
+
+    @Override
+    protected String getInputType() {
+        return "radio";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e1feeb22/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/SpringFormTemplateCallableHashModel.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/SpringFormTemplateCallableHashModel.java
b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/SpringFormTemplateCallableHashModel.java
index a2750dc..8e28c4c 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/SpringFormTemplateCallableHashModel.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/SpringFormTemplateCallableHashModel.java
@@ -59,6 +59,7 @@ public final class SpringFormTemplateCallableHashModel implements TemplateHashMo
         modelsMap.put(CheckboxTemplateDirectiveModel.NAME, new CheckboxTemplateDirectiveModel(request,
response));
         modelsMap.put(CheckboxesTemplateDirectiveModel.NAME, new CheckboxesTemplateDirectiveModel(request,
response));
         modelsMap.put(RadioButtonTemplateDirectiveModel.NAME, new RadioButtonTemplateDirectiveModel(request,
response));
+        modelsMap.put(RadioButtonsTemplateDirectiveModel.NAME, new RadioButtonsTemplateDirectiveModel(request,
response));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e1feeb22/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/form/RadioButtonsTemplateDirectiveModelTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/form/RadioButtonsTemplateDirectiveModelTest.java
b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/form/RadioButtonsTemplateDirectiveModelTest.java
new file mode 100644
index 0000000..624a9ed
--- /dev/null
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/form/RadioButtonsTemplateDirectiveModelTest.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.spring.model.form;
+
+import org.apache.freemarker.spring.example.mvc.users.User;
+import org.apache.freemarker.spring.example.mvc.users.UserRepository;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.xpath;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@WebAppConfiguration("classpath:META-INF/web-resources")
+@ContextConfiguration(locations = { "classpath:org/apache/freemarker/spring/example/mvc/users/users-mvc-context.xml"
})
+public class RadioButtonsTemplateDirectiveModelTest {
+
+    @Autowired
+    private WebApplicationContext wac;
+
+    @Autowired
+    private UserRepository userRepository;
+
+    private MockMvc mockMvc;
+
+    @Before
+    public void setUp() {
+        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
+    }
+
+    @Test
+    public void testRadioButtonsWithSequence() throws Exception {
+        final User user = userRepository.getUserByEmail("jane@example.com");
+        mockMvc.perform(get("/users/{userId}/", user.getId()).param("viewName", "test/model/form/radiobuttons-directive-usages")
+                .accept(MediaType.parseMediaType("text/html"))).andExpect(status().isOk())
+                .andExpect(content().contentTypeCompatibleWith("text/html")).andDo(print())
+                .andExpect(xpath("//form[@id='form1']//span/input[@type='radio' and @id='genderInForm11'
and @name='gender']/@value").string("F"))
+                .andExpect(xpath("//form[@id='form1']//span/input[@type='radio' and @id='genderInForm11'
and @name='gender']/@checked").string("checked"))
+                .andExpect(xpath("//form[@id='form1']//span/label[@for='genderInForm11']").string("F"))
+                .andExpect(xpath("//form[@id='form1']//span/input[@type='radio' and @id='genderInForm12'
and @name='gender']/@value").string("M"))
+                .andExpect(xpath("//form[@id='form1']//span/input[@type='radio' and @id='genderInForm12'
and @name='gender']/@checked").doesNotExist())
+                .andExpect(xpath("//form[@id='form1']//span/label[@for='genderInForm12']").string("M"))
+                .andExpect(xpath("//form[@id='form1']//span/input[@type='radio' and @id='genderInForm13'
and @name='gender']/@value").string("U"))
+                .andExpect(xpath("//form[@id='form1']//span/input[@type='radio' and @id='genderInForm13'
and @name='gender']/@checked").doesNotExist())
+                .andExpect(xpath("//form[@id='form1']//span/label[@for='genderInForm13']").string("U"));
+    }
+
+    @Test
+    public void testRadioButtonsWithHash() throws Exception {
+        final User user = userRepository.getUserByEmail("john@example.com");
+        mockMvc.perform(get("/users/{userId}/", user.getId()).param("viewName", "test/model/form/radiobuttons-directive-usages")
+                .accept(MediaType.parseMediaType("text/html"))).andExpect(status().isOk())
+                .andExpect(content().contentTypeCompatibleWith("text/html")).andDo(print())
+                .andExpect(xpath("//form[@id='form2']//span/input[@type='radio' and @id='genderInForm21'
and @name='gender']/@value").string("F"))
+                .andExpect(xpath("//form[@id='form2']//span/input[@type='radio' and @id='genderInForm21'
and @name='gender']/@checked").doesNotExist())
+                .andExpect(xpath("//form[@id='form2']//span/label[@for='genderInForm21']").string("Female"))
+                .andExpect(xpath("//form[@id='form2']//span/input[@type='radio' and @id='genderInForm22'
and @name='gender']/@value").string("M"))
+                .andExpect(xpath("//form[@id='form2']//span/input[@type='radio' and @id='genderInForm22'
and @name='gender']/@checked").string("checked"))
+                .andExpect(xpath("//form[@id='form2']//span/label[@for='genderInForm22']").string("Male"))
+                .andExpect(xpath("//form[@id='form2']//span/input[@type='radio' and @id='genderInForm23'
and @name='gender']/@value").string("U"))
+                .andExpect(xpath("//form[@id='form2']//span/input[@type='radio' and @id='genderInForm23'
and @name='gender']/@checked").doesNotExist())
+                .andExpect(xpath("//form[@id='form2']//span/label[@for='genderInForm23']").string("Unspecified"));
+    }
+
+    @Test
+    public void testRadioButtonsWithEnclosingElementNameAndDelimiter() throws Exception {
+        final User user = userRepository.getUserByEmail("john@example.com");
+        mockMvc.perform(get("/users/{userId}/", user.getId()).param("viewName", "test/model/form/radiobuttons-directive-usages")
+                .accept(MediaType.parseMediaType("text/html"))).andExpect(status().isOk())
+                .andExpect(content().contentTypeCompatibleWith("text/html")).andDo(print())
+                .andExpect(xpath("//form[@id='form3']//div/input[@type='radio' and @id='genderInForm31'
and @name='gender']/@value").string("F"))
+                .andExpect(xpath("//form[@id='form3']//div/input[@type='radio' and @id='genderInForm31'
and @name='gender']/@checked").doesNotExist())
+                .andExpect(xpath("//form[@id='form3']//div/label[@for='genderInForm31']").string("Female"))
+                .andExpect(xpath("//form[@id='form3']//div[1]/br").doesNotExist())
+                .andExpect(xpath("//form[@id='form3']//div/input[@type='radio' and @id='genderInForm32'
and @name='gender']/@value").string("M"))
+                .andExpect(xpath("//form[@id='form3']//div/input[@type='radio' and @id='genderInForm32'
and @name='gender']/@checked").string("checked"))
+                .andExpect(xpath("//form[@id='form3']//div/label[@for='genderInForm32']").string("Male"))
+                .andExpect(xpath("//form[@id='form3']//div[2]/br").exists())
+                .andExpect(xpath("//form[@id='form3']//div/input[@type='radio' and @id='genderInForm33'
and @name='gender']/@value").string("U"))
+                .andExpect(xpath("//form[@id='form3']//div/input[@type='radio' and @id='genderInForm33'
and @name='gender']/@checked").doesNotExist())
+                .andExpect(xpath("//form[@id='form3']//div/label[@for='genderInForm33']").string("Unspecified"))
+                .andExpect(xpath("//form[@id='form3']//div[3]/br").exists());
+    }
+}

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e1feeb22/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/radiobuttons-directive-usages.f3ah
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/radiobuttons-directive-usages.f3ah
b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/radiobuttons-directive-usages.f3ah
new file mode 100644
index 0000000..051ae47
--- /dev/null
+++ b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/radiobuttons-directive-usages.f3ah
@@ -0,0 +1,83 @@
+<#--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<html>
+<body>
+
+  <h1>Form 1</h1>
+  <hr/>
+  <form id="form1">
+    <table>
+      <tr>
+        <th>User:</th>
+        <td>
+          ${user.firstName} ${user.lastName} (${user.email})
+        </td>
+      </tr>
+      <tr>
+        <th>Gender</th>
+        <td>
+          <#assign genders = [ "F", "M", "U" ] >
+          <@form.radiobuttons "user.gender" id="genderInForm1" items=genders />
+        </td>
+      </tr>
+    </table>
+  </form>
+
+  <h1>Form 2</h1>
+  <hr/>
+  <form id="form2">
+    <table>
+      <tr>
+        <th>User:</th>
+        <td>
+          ${user.firstName} ${user.lastName} (${user.email})
+        </td>
+      </tr>
+      <tr>
+        <th>Gender</th>
+        <td>
+          <#assign genderHash = { "F": "Female", "M": "Male", "U": "Unspecified" } >
+          <@form.radiobuttons "user.gender" id="genderInForm2" items=genderHash />
+        </td>
+      </tr>
+    </table>
+  </form>
+
+  <h1>Form 3</h1>
+  <hr/>
+  <form id="form3">
+    <table>
+      <tr>
+        <th>User:</th>
+        <td>
+          ${user.firstName} ${user.lastName} (${user.email})
+        </td>
+      </tr>
+      <tr>
+        <th>Gender</th>
+        <td>
+          <#assign genderHash = { "F": "Female", "M": "Male", "U": "Unspecified" } >
+          <@form.radiobuttons "user.gender" id="genderInForm3" items=genderHash element="div"
delimiter="<br/>" />
+        </td>
+      </tr>
+    </table>
+  </form>
+
+</body>
+</html>


Mime
View raw message