Scott Hanselman

NuGet Package of the Week: A different take on ASP.NET MVC Forms with ChameleonForms

February 17, '15 Comments [32] Posted in ASP.NET MVC | NuGet | NuGetPOW | Open Source
Sponsored By

One of the nice things about any modular system (like ASP.NET) is the ability to swap out the parts you don't like. As the authors of ChameleonForms state, HTML forms is a pain. It's repetitive, it's repetitive, and it's boring. While ASP.NET MVC's Form Helpers help a lot, they felt that helper methods like Html.EditorForModel didn't go far enough or give you enough flexibility. ChameleonForms adds its own templating model and attempts to be as DRY as possible. It also takes a number of issues head on like better handling for drop-down lists and lists of radio buttons, and it even supports Twitter Bootstrap 3 to you can bang out HTML forms ASAP.

ChameleonForms also is a nice example of a tidy and well-run small open source project. They've got a public Trello backlog board, excellent documentation, a continuous integration build, a good example project, and of course, they're on NuGet. Check out the other projects that the folks in the "MRCollective" work on as well, as they've got their own GitHub organization.

NuGet Install ChameleonForms

Often ChameleonForms tries to use C# for the whole form, rather than switching back and forth from Div to Html Helper. For example:

@using (var f = Html.BeginChameleonForm()) {
using (var s = f.BeginSection("Signup for an account")) {
@s.FieldFor(m => m.FirstName)
@s.FieldFor(m => m.LastName)
@s.FieldFor(m => m.Mobile).Placeholder("04XX XXX XXX")
@s.FieldFor(m => m.LicenseAgreement).InlineLabel("I agree to the terms and conditions")
}
using (var n = f.BeginNavigation()) {
@n.Submit("Create")
}
}

This is the whole form using usings for scoping, and it's nice and clean.  How about a comparison example? Here's standard ASP.NET MVC:

@using (Html.BeginForm())
{
<fieldset>
<legend>A form</legend>
<dl>
<dt>@Html.LabelFor(m => m.RequiredString, "Some string")</dt>
<dd>@Html.TextBoxFor(m => m.RequiredString) @Html.ValidationMessageFor(m => m.RequiredString)</dd>
<dt>@Html.LabelFor(m => m.SomeEnum)</dt>
<dd>@Html.DropDownListFor(m => m.SomeEnum, Enum.GetNames(typeof(SomeEnum)).Select(x => new SelectListItem {Text = ((SomeEnum)Enum.Parse(typeof(SomeEnum), x)).Humanize(), Value = x})) @Html.ValidationMessageFor(m => m.SomeEnum)</dd>
<dt>@Html.LabelFor(m => m.SomeCheckbox)</dt>
<dd>@Html.CheckBoxFor(m => m.SomeCheckbox) @Html.LabelFor(m => m.SomeCheckbox, "Are you sure?") @Html.ValidationMessageFor(m => m.SomeCheckbox)</dd>
</dl>
</fieldset>
<div class="form_navigation">
<input type="submit" value="Submit" />
</div>
}

And here is the same form with ChameleonForms.

@using (var f = Html.BeginChameleonForm()) {
using (var s = f.BeginSection("A form")) {
@s.FieldFor(m => m.RequiredString).Label("Some string")
@s.FieldFor(m => m.SomeEnum)
@s.FieldFor(m => m.SomeCheckbox).InlineLabel("Are you sure?")
}
using (var n = f.BeginNavigation()) {
@n.Submit("Submit")
}
}

But these are basic. How about something more complex? This one has a bunch of variety, a number overloads and customizations, as well as a FileUpload (note that the form is a Multipart form):

@using (var f = Html.BeginChameleonForm(method: FormMethod.Post, enctype: EncType.Multipart))
{
<p>@f.LabelFor(m => m.SomeCheckbox).Label("Are you ready for: ") @f.FieldElementFor(m => m.SomeCheckbox) @f.ValidationMessageFor(m => m.SomeCheckbox)</p>
<p>@f.FieldElementFor(m => m.RequiredStringField).TabIndex(4)</p>
using (var s = f.BeginSection("My Section!", InstructionalText(), new{@class = "aClass"}.ToHtmlAttributes()))
{
using (var ff = s.BeginFieldFor(m => m.RequiredStringField, Field.Configure().Attr("data-some-attr", "value").TabIndex(3)))
{
@ff.FieldFor(m => m.NestedField).Attr("data-attr1", "value").TabIndex(2)
@ff.FieldFor(m => m.SomeEnum).Attr("data-attr1", "value")
@ff.FieldFor(m => m.SomeEnum).Exclude(SomeEnum.SomeOtherValue)
}
@s.FieldFor(m => m.SomeCheckbox).AsDropDown()
using (var ss = s.BeginSection("Nested section"))
{
@ss.FieldFor(m => m.FileUpload).Attr("data-attr1", "value")
}
@s.FieldFor(m => m.RequiredStringField).OverrideFieldHtml(new MvcHtmlString("Custom html <b>she-yeah</b>!"))
@s.FieldFor(m => m.TextAreaField).Cols(60).Rows(5).Label("Some Label").AutoFocus().TabIndex(1)
@s.FieldFor(m => m.SomeCheckbox).InlineLabel("Some label").WithHint("Format: XXX")
@s.FieldFor(m => m.SomeCheckbox).AsRadioList().WithTrueAs("True").WithFalseAs("False")
@s.FieldFor(m => m.ListId)
@s.FieldFor(m => m.ListId).AsRadioList()
@s.FieldFor(m => m.SomeEnums)
@s.FieldFor(m => m.SomeEnumsList).AsRadioList()
@s.FieldFor(m => m.Decimal)
@s.FieldFor(m => m.Int).AsInputGroup().Append(".00").Prepend("$")
@s.FieldFor(m => m.DecimalWithFormatStringAttribute)
@s.FieldFor(m => m.NullableInt)
@s.FieldFor(m => m.Child.ChildField)
@s.FieldFor(m => m.Child.SomeEnum).AsRadioList()
@s.FieldFor(m => m.RequiredStringField).Disabled()
@s.FieldFor(m => m.RequiredStringField).Readonly()
}
using (var n = f.BeginNavigation())
{
@n.Submit("Submit")
@n.Reset("Reset")
}
}

ChameleonForms also has a special NuGet package if you're using TwitterBootstrap that changes how forms with the BeginChameleonForm method render.

ChameleonForms also has some convenient extra abilities, like being able to automatically infer/create a [DisplayName] so you don't have to. If you're doing Forms in English and your preferred Display Name will end up just being your variable name this can be a useful time saver (although you may have opinions about its purity.)

So instead of the tedium of:

[DisplayName("Email address")]
public string EmailAddress { get; set; }

[DisplayName("First name")]
public string FirstName { get; set; }

You can just say this once, picking just one...this is an example where they use HumanizedLabels.

HumanizedLabels.Register(LetterCasing.AllCaps) => "EMAIL ADDRESS"
HumanizedLabels.Register(LetterCasing.LowerCase) => "email address"
HumanizedLabels.Register(LetterCasing.Sentence) => "Email address"
HumanizedLabels.Register(LetterCasing.Title) => "Email Address"

If you've got a lot of Forms to create and they're just no fun anymore, you should definitely give ChameleonForms a try. If you're a Twitter Bootstrap shop, doubly so, as that's where ChameleonForms really shines.

I'll do a few other posts exploring different ways to for Forms in ASP.NET MVC in the coming weeks. Be sure to explore the NuGet Package of the Week Archives as well!


PLUG: Did you know I have a YouTube channel? Subscribe over here. I've got tutorials on how to effectively use Windows 8 and 8.1, Build to Build walkthroughs of the latest versions of Windows 10, and I just started a new series I'm sure you'll want to share with your family called "How to REALLY use Microsoft Office." Help me out and spread the word!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb
Tuesday, 17 February 2015 09:26:06 UTC
Good luck getting the designer to understand to C# code. The standard ASP.NET MVC forms can be easily understood by designers and coders alike, they can work together without stepping on anybody's toe..
rosdi
Tuesday, 17 February 2015 09:28:03 UTC
For those in the MVC crowd. I work on the open source ASP.NET MVC Boilerplate project on GitHub.

It's a professional ASP.NET MVC template that helps you get over 97/100 on YSlow right out of the box! All while being secure out of the box using extra HTTP headers like Content-Security-Policy (CSP) etc.

It also includes popular NuGet packages like Elmah (Error Handling and Logging), Glimpse (Easier Debugging), NWebsec (Security) and Autofac (IOC Container), all preconfigured and ready to go right out the box!
Tuesday, 17 February 2015 09:32:56 UTC
Hi Scott,

Nice find! This seems to work in a similar manor as Fubu Html Tags which has an aim of providing a 'JQuery like syntax' for creating HTML elements in C#. I've found this to be a helpful tool to use for my own HTML helpers. Would be interested to know if these guys use this library under the hood.

I could be wrong, but it doesn't seem like ChameleonForms has any support for validation, like MVC does. However, I can see that it is on their Trello board though!

Dan
Daniel Mackay
Tuesday, 17 February 2015 09:38:20 UTC
Hi rosdi,

I'm one of the ChameleonForms developers.

One of the things that you get with ChameleonForms is consistency across all of your forms. One of the problems we often find with forms is that they are tedious in terms of markup and you will often miss out tags here and there causing the forms to look slightly different. ChameleonForms takes that possibility away because it abstracts the form template for you to ensure consistency.

You will probably get your designers to help write the html for the template (example here) and to write the CSS. If they are writing the form itself, then they would need a similar knowledge for using the MVC helpers as they would for Chameleon - they are very similar except Chameleon has less HTML markup scattered across the form (not that there is anything stopping you adding HTML markup for those rare situations that need it because the standard template doesn't cover it). We find that intellisense is your friend and generally it means it's a lot easier to quickly whip up a form as compared to having to remember a bunch of HTML markup.
Tuesday, 17 February 2015 09:42:34 UTC
Hi Daniel,

We don't use Fubu - we have our own concept called field configuration.

We support the validation that is built-in with MVC - it's added automatically as part of the template (that comes by default, or you can customise). The items on the trello board I suspect you are referring to are our original strategy and are actually done :)
Tuesday, 17 February 2015 09:49:14 UTC
Looks cool, really.

But can't say it's more readable than normal razor-syntax. As one who sees the finished code for the multipart form-example for the first time, it feels harder to read and understand what the result will actually be. Though I guess it's much more straightforward when I'm the one writing it from the beginning.

(I'm not saying not to use it. It's just an observation.)
Tuesday, 17 February 2015 09:53:57 UTC
This looks pretty good in the current ASP.NET MVC world. But it is the complete opposite of how you would do things in ASP.NET MVC 6 with tag helpers. However I like that you could go all out and design your forms with code (using this approach) or with html (using tag helpers).
Lee Timmins
Tuesday, 17 February 2015 10:06:27 UTC
Hey Daniel,

There's also some additional validation you can get out of the box, for example:

https://github.com/MRCollective/ChameleonForms/wiki/list (validating that user selected items exist in a given list property, or in an enum)

https://github.com/MRCollective/ChameleonForms/wiki/datetime (extends DisplayFormat to parse and validate a submitted date on the server)
Tuesday, 17 February 2015 10:11:28 UTC
I wish people would stop trying to reinvent HTML.

HTML is easy to code & easy for everyone (including designers) to understand. A designer would not have any idea how to modify the forms in the example & it just complicates things for no reasons at all.

This reminds me of asp.net web forms & is what is wrong with asp.net in general.. PLEASE DO NOT TOUCH MY HTML & STOP MAKING THINGS MORE COMPLICATED THAN THEY NEED TO BE....
Adam
Tuesday, 17 February 2015 10:47:50 UTC
Seems like an opportune place to pimp my own form builder, which transforms an object model (or controller action arguments) into a form model, and then into a form using overrideable templates, automatically creating datepickers, autocompletes and select lists (and more) as appropriate.

http://formfactory.apphb.com/
https://github.com/mcintyre321/FormFactory
Harry McIntyre
Tuesday, 17 February 2015 12:10:52 UTC
Hi Adam,

Thanks for the feedback!

We find it often makes sense to template your forms in one place, allowing your models to drive the HTML that's produced. This gives you consistency between all the forms in your application, all the usual benefits of type safety, as well as the ability to customise individual fields when you do run into a special case.

In saying that, the right tool for the job always depends on your environment. When I've had designers and developers working on the same application before, I've seen great results splitting the frontend content completely from the backend code using a framework like AngularJS.
Tuesday, 17 February 2015 13:23:37 UTC
Very nice, will give it a shot for small level projects.

Tuesday, 17 February 2015 13:56:50 UTC
@Matt Davies

We find it often makes sense to template your forms in one place, allowing your models to drive the HTML that's produced.


We have that. They're called display and editor templates. They've been around since ASP.NET MVC 2 and manage to work for that purpose without shaping C# and method chaining into a contorted DSL that obfuscates the rendered end-result.
ron
Tuesday, 17 February 2015 15:08:54 UTC
Hi ron,

Don't particularly want to start a debate, but we cover why we think ChameleonForms is needed including some discussion about display and editor templates in the documentation.

We're pretty happy with the DSL, find it very quick and easy to read and write and have had a lot of success using it across a number of projects for quite a number of years in both it's current incarnation and a previous one it was based on that we developed in a former company we worked for. We understand it's not for everyone and like Matt said above, we are firm believers in right tool for the job be it plain HTML, Angular JS forms, Editor templates or ChameleonForms :) If you have a technique that works for you then awesome!!
Tuesday, 17 February 2015 15:13:19 UTC
I'm going to agree with Adam on this one. I thought one of the complaints about web forms was the level of abstraction it was putting between standard web development and now in this example we're adding it back. I guess I'm getting a little overloaded with the number of extensions/plugins/frameworks like angular, backbonejs, knockout, ember, then a gazillion mvc view engines, then 14 different options for nosql. Don't get me wrong options are good but within reason. Obviously, no one is forcing me to use every new technology that comes out but when trying to keep up to date with my skills it's a bit much when there is so much new coming out every single day.
Darrell
Tuesday, 17 February 2015 16:36:16 UTC
If HTML is the language understood by browsers, and it is accepted as the "standard" for whatever that means, why do we keep building abstractions on top of it? I can see some value in this kind of a framework, but doesn't the fragmentation of standards (e.g. HTML isn't good enough, let's build another abstraction layer) hinder programming as a field?

That said, the implementation of this idea is wonderfully solid, I just have issues with the idea that it is necessary.
Tuesday, 17 February 2015 18:21:15 UTC
@Matthew: I might ask you: If CIL bytecode is the language understood by the CLR, and it is accepted as the "standard" for whatever that means, why do we keep building abstractions on top of it?

Having a domain-specific language or good framework for spitting out a specific structure of HTML does not seem like an attempt to subvert HTML - it seems like an attempt to make using it richly more simple.
Jesper
Tuesday, 17 February 2015 19:19:21 UTC
I think I'm in the Shut Up and Take My (Imaginary) Money camp on Chameleon. I can much more easily understand that syntax. I'd much rather write less code. There is less opportunity for something to not look/work right by my own hand. I'll have to give it a shot.
John Dunagan
Wednesday, 18 February 2015 05:57:51 UTC
Really Nice. Will going to try it for next project for sure!
Wednesday, 18 February 2015 08:01:39 UTC
I'm sure this can be useful for some, but personally I'm not a big fan of this direction. I want my cshtml to contain more html and less cs. The whole idea of generating html from server-side code brings back nightmares of WebForms server controls, which really only works well if you don't care about the quality and format of the generated markup.
In the new world of web development, we need complete control of the markup. And I mean COMPLETE control. I don't even want C# code to generate textboxes or labels for me, even if it's convenient. And even though the helpers can be overridden and any attributes can be added, it's a matter of principle. And as someone mentioned, it seems to go against the idea of ASP.NET MVC 6.
John
Wednesday, 18 February 2015 13:29:03 UTC
One of the major selling points of ASP.NET MVC to my designer colleagues is that "it's just HTML".
As a developer I see the advantage of this, but there's no chance in hell this will even be looked at by a web designer.
Wednesday, 18 February 2015 14:33:31 UTC
You should only use the tools, frameworks, plugins, add-ins and languages that make sense to you and the project at hand. If you're happy writing static HTML and CSS for your project/site then fine. If you want to go all out and use C# code to generate HTML then fine again. People complain about the new StatusQuo of ASP.NET 5 and MVC 6 and that this package doesn't fit. Remember that there are thousands upon thousands of sites that will not be upgraded to the new framework but will need new features and functionality added. ChamaleonForms will fit nicely in that model. And I'm sure there will be some new NuGet package that will offer something similar in MVC6. No one's forcing you to use any tools that don't meet your real needs, especially when you are writing production code. Choose wisely and make sure you always have fun...
Wednesday, 18 February 2015 15:01:27 UTC
I don't get the people saying that the frontend developers run when the see C# code. They don't run from javascript (and in Razor, C# can be said it resembles js), php or ruby code (helpers). A frontend dev can learn Razor and the minimum C# syntax quite fast, for them it's just some form of js.

And with intellisense it's even easier if they are open minded enough to use VS (now even more free than before) instead of whatever notepad clone. I like the 'ChameleonForms' (IMO you need a better name) idea and any frontdev worth their salt won't have a problem with it.

That being said I'm going to continue to use Fubu's HtmlTags and my MvcPowerTools, which allow me to define in code html conventions (widgets) based on the model/property type. I'm finding this approach more maintainable than (re)writing each form line by line, which I think it's the Chameleon's biggest flaw.
Thursday, 19 February 2015 00:56:33 UTC
Would be nice to see examples of web pages using ChameleonForms and javascript frameworks such as Angular, Bootstrap, Knockout, etc.
Steve G
Thursday, 19 February 2015 21:52:08 UTC
I'm with others where I want more HTML in my projects, not less.

Probably would not use this as it ads more overhead than I'd like. The file upload example looks like a maintenance nightmare. Even with the DSL when I'm writing code in VS I don't want to have to assume what my "rendered" HTML is going to look like. A lot of sites I'm building these days are built with AJAX/JS templates anyway.

Like Scott said nicely run project though seems it serves a purpose for some!
Friday, 20 February 2015 09:20:01 UTC
Rob, Matt

Nice to see the Scott giving ChameleonForms some airtime, I've used it on a bunch of stuff and it's great for the LOB work I do most of the time.

Checkout what these guys repo https://github.com/MRCollective for other cool stuff they are into I really liked the Octopus work too.
Tuesday, 24 February 2015 16:08:33 UTC
Nothing compares to pure HTML :-)
Rex.
Tuesday, 24 February 2015 16:43:10 UTC
To each their own. I personally hate HTML helpers and prefer to stay as close to writing plain HTML as possible. Really looking forward to trying out tag helpers in vNext.
Thursday, 26 February 2015 10:55:55 UTC
@Vesselin - I'm with you & also hate the HTML helpers. TBH: I'm not sure about the tag helpers in vnext as I think the tag helpers just look like ordinary HTML so it will just confuse people. All server side code should be after a @ - something like: @Taghelper("Test1","Test2","Test3");

I also have to say that most .net programmers only know c# well & very few know html & javascript properly.. In today's world html & javascript should be the most important languages to use websites & web apps. C# should only be used for back end stuff like getting/saving data to databases.
Scott
Thursday, 26 February 2015 15:33:39 UTC
should use t4 to create consistent form design rather than go back to c#
fu
Sunday, 08 March 2015 00:14:19 UTC
I recomend BForms:

The BForms open source framework consists of a collection of ASP.NET MVC HTML Helpers, Javascript AMD modules, custom CSS made with SASS that extends Twitter Bootstrap v3 and makes development of rich user interfaces easy.

BForms Documentation
Marius Stratulat
Monday, 16 March 2015 07:29:31 UTC
After working with Angular Js and SPA I use razor only for passing initialization data on the client like the Antiforgery token, the flexibility I have by separating the html from the server makes all server forms obsolete.
Enrique
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.