[Coding] ASP.NET, JavaScript – More Form Quirks

Posted by Khatharsis on January 24, 2014

I was just talking to some friends about my new-ish job. I am happiest when I have a series of problems that are solvable, but the answer may not be evident right away. It’s sort of like puzzle-solving. The solutions are there (on the internet), but for your specific problem, you have to massage existing solutions to fit.

I just found out that ASP.NET won’t take any disabled .NET form fields (e.g., TextBox, DropDownList) on postback. Another weird quirk is changing a .NET Button control’s text via JavaScript isn’t recognized in postback, either. This seems a little conflicting, no? What makes it even worse is when .NET’s validation controls (RequiredFieldValidtor) are thrown into the mix–on postback, all of the validation error messages pop up.

The ASP.NET code looks something like the following:

	<asp:TextBox ID="TextBoxName" runat="server" />
	<asp:RequiredFieldValidator ID="RequiredFieldValidatorName" runat="server"
		ControlToValidate="TextBoxName" ErrorMessage="Name cannot be empty" />
	<br />
	<asp:TextBox ID="TextBoxEmail" runat="server" />
	<asp:RequiredFieldValidator ID="RequiredFieldValidatorEmail" runat="server"
		ControlToValidate="TextBoxEmail" ErrorMessage="Email cannot be empty" />
	<br />
	<asp:DropDownList ID="DropDownListRole" runat="server">
		<asp:ListItem Value="" Text="--Select a Role--" />
		<asp:ListItem Value="W" Text="Wookiee" />
		<asp:ListItem Value="J" Text="Jedi" />
	<br />
	</asp:DropDownList>
	<asp:HiddenField ID="HiddenFieldRole" runat="server" />
	<asp:RequiredFieldValidator ID="RequiredFieldValidatorRole" runat="server"
		ControlToValidate="DropDownListRole" ErrorMessage="Must select a role" />
	
	<asp:Button ID="ButtonSubmit" runat="server" Text="Enter New User"
		onclick="ButtonSubmit_Click" />

Fairly simple, two text fields, a dropdown list, and a hidden field. Each form element except for the hidden field has a RequiredFieldValidator attached to it. The one for the dropdown list will display the error message when Value=””, or the first option. The basic functionality of this form is to create a new user.

It gets a little more complicated. The same form can edit existing users via a text link. However, only the Email field can be edited. The JavaScript code looks a bit like (I was lazy to include ClientIDMode=”Static” in the ASP.NET code above, but that is present in all ASP.NET controls):

// Binds together the hidden field to the drop down list
$('#DropDownListRole').change(function() {
	$('#HiddenFieldRole').val($('#DropDownListRole').val());
});

// Just for demonstration sake, assume there is a text link with id="wookiee"
// (Yes, I know this is not something you'd actually do.)
$('#wookiee').click(function() {
	$('#TextBoxName').val('Chewbacca');
	$('#TextBoxEmail').val('chewyis@chewy.com');
	$('#DropDownListRole').val('W');
	$('#HiddenFieldRole').val('W');
	
	$('#TextBoxName').prop('readonly', true); // NOTE!
	$('#DropDownListRole').prop('disabled', true);
});

I wasn’t aware readonly was an HTML property until I ran into this postback problem. ASP.NET will not send any form elements that have been disabled. The workaround, for text boxes, is to use the readonly property. I also had to add in some CSS to make it visually clear that it wasn’t an editable field.

Problem is, the dropdown list doesn’t have this readonly property, so it needs a separate workaround – the HiddenField element. I added an onChange event handler so that every time the dropdown list’s selected item is changed, the HiddenField element picks it up.

Okay, now the code behind (C#):

protected void ButtonSubmit_Click(object sender, EventArgs e)
{
	string login = TextBoxName.Text;
	string email = TextBoxEmail.Text;
	string role = DropDownListRole.SelectedValue;
	
	// Some magic occurs with these values
	
	// Clear fields
	TextBoxName.Text = "";
	TextBoxName.Email = "";
	DropDownListRole.SelectedIndex = 0;
}

In the context of this particular form, I find it’s a little tacky to postback with the form elements filled out with values I just submitted. If I wanted to create a few users back-to-back, having an empty form to work with is ideal.

Everything works great except for one thing. Hitting the button and the page reloads for postback and the user sees an empty form. With an error message next to the dropdown list. orz

I understand that this validation message pops up after postback because it is the server-side validation (as opposed to client-side). The reason why I’m using ASP.NET’s validators is because of this dual client- and server-side validation that I don’t have to write duplicate code for. What I don’t understand is why an exception is thrown when I try to validate the hidden field, or for that matter, why an ASP.NET hidden field validator still does not exist, requiring users to make their own workarounds.

The workaround I eventually went with was a mashup of StackOverflow comments (see a few examples). I went with Peter’s workaround, creating a HiddenFieldValidator class (under Models -> Controls as I didn’t want to add another directory in the root folder as it’s already populated with DAL, Controllers, Models, and Scripts, but this is subject to change). I had to spend some time configuring Web.config to work properly as the examples provided were not very clear. Hopefully this helps:


<system.web>
<pages>
<controls>
<add tagPrefix="custom" namespace="AppName.Models.Controls" assembly="AppName">
<controls>
</pages>
</system.web>

Where tagPrefix is of your choice. Just as ASP.NET controls are prefixed by “asp:”, the example above would have the prefix “custom:”. As I explained above, I put the HiddenFieldValidator class in Models/Controls folder/namespace. AppName is whatever the name of your web app is. Together, they comprise “AppName.Models.Controls”. The assembly name is simply the name of your web app; here, it is “AppName”.

Then,

<asp:RequiredFieldValidator ID="RequiredFieldValidatorRole" runat="server"
ControlToValidate="DropDownListRole" ErrorMessage="Must select a role" />

Changes to:

<custom:HiddenFieldValidator ID="HiddenFieldValidatorRole" runat="server"
ControlToValidate="HiddenFieldRole" ErrorMessage="Must select a role" />

These quirks make ASP.NET feel constricting and lacking to me. At least there are workarounds.