Dynamic forms with ASP.NET MVC and XML
June 28, 2009A number of times in the past I’ve had the inclination to have a web form generated dynamically based on a templating system. Far too many times I’ve had to deal with changing requirements on large forms with tons of input elements, and it’s such a headache to modify them, particularly with ASP.NET Webforms.
The problem with Webforms
While ASP.NET always had the capability of dynamic forms, it almost always turns out to be such a pain in the ass it’s not even remotely worth the hassle. Use of dynamic ASP.NET web Controls is frustration defined.
With ASP.NET MVC (as well as new C# language features Linq to XML) we finally have the technology in place for implementing dynamic forms with ease.
XML to HTML
The idea here is to define all of your form fields (text box, radio buttons, drop down lists, etc) in an XML document while the .aspx View page dynamically renders it into Html. If any change is required to the form fields (changing an attribute, or even adding/removing a field entirely – more on that later) it can be done to the definition in the XML document. No need to touch the View page, and certainly no need to touch code.
Below is my form elements represented by XML. This structure and naming convention was entirely… uh, pulled out of my ass for this demonstration. Certainly this is not complete enough for any real world usage. (Notes below)
What I’ve done is defined a root “form” element with a number of “sections,” where each section will represent an HTML <fieldset>. Each field within has attributes for type, name and css class. I’ll even be supporting default values – as you can see I’ve inputted a value for the Biography field.
In this example I’m only dealing with input of type text, an obvious cop out since this will be the most simple and straightforward example. There’s nothing preventing you from representing any of the other HTML input elements here though!
Building the form
The controller action is really simple. All we need to do is pass the View the XML structure so it can build the form. Down and dirty, I’m just stashing an XElement containing the XML into ViewData. We’ll take care of everything else in the View page.
Controller action (GET)
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index()
{
//load xml
XElement xml = XElement.Load(Server.MapPath("~/App_Data/form1.xml"));
ViewData["xml"] = xml;
return View();
}
Let’s pause for a word of warning: the following view page is not exactly the prettiest, but it will get the job done for now. See if you can interpret what’s going on, then read the notes that follow.
<% XElement xml = (XElement)ViewData["xml"]; %>
<h2>XML-based Dynamic Form</h2>
<%Html.BeginForm(); %>
<%var q = from s in xml.Descendants("section")
select s; %>
<%foreach(XElement sec in q) { %>
<fieldset>
<legend>Section <%=sec.Attribute("number").Value %></legend>
<table>
<%var qq = from f in sec.Descendants("field")
select f; %>
<%foreach(XElement field in qq) { %>
<tr>
<td><%=field.Attribute("name").Value %></td>
<td>
<%=Html.TextBox(field.Attribute("name").Value, field.Value, new { @class = field.Attribute("class").Value })%>
</td>
</tr>
<%} %>
</table>
</fieldset>
<%} %>
<input type="submit" value="Submit!" />
<%Html.EndForm(); %>
What’s happening here is fairly simple. We have an outer-loop that is iterating over the “section” elements in our XML (there are two of them, numbered 1 and 2) and create a fieldset that will contain a table to house our form fields. The inner loop is iterating over all of the field elements within the current section. I’m using the value of the name attribute as the description for the field, and I’m creating an HTML textbox with a name of the name attribute, a value of the field element’s value (if present), and a class attribute from the attribute of the same name.
The rendered form
Review the XML above and see how it came out on the rendered form. Note that the Biography input has the pre-set value as determined in the XML. Why is it a different size? It’s been assigned a css class that has set the height and width as shown.
When the Submit button is clicked the name/value pairs of each of these form elements will be posted to the appropriate controller action, where the rest of the magic will happen.
To be continued?
As cool as this may be, we’ve only implemented half of the solution. What use is a dynamic form if the controller that it posts to is stupid? We could bind this form to an arbitrary object or pick out form collection values, but what are we even looking for? We can’t hard-code the name values to retrieve or the application will blow up as soon as a form element or name changes!
Perhaps in a future segment we’ll address this issue…
Tags: dynamic forms, xml, mvc
Categories: ASP.NET MVC, C#, HTML