ASP.Net MVC 2.0 RC 2, Input Validation and Breaking Our App 0

It’s been a while since I’ve posted and I’m trying to get back into the swing of things again. I don’t really have a lot to say about the problem, just yet. We’ve identified some breaking changes with ASP.Net MVC 2.0 RC 2.

RC 2 switches to relying on all data annotations on the model when doing validation, when previous releases only checked the values posted and properly bound to a model. To be honest, initially, I didn’t like that idea. It was confusion to me why my model was attributed with attributes requiring fields, but validation was not happening as I expected.

Over time, I came to appreciate, and apparently exploit this feature to do conditional validation. Since the form only posts the enabled controls, I was able to simply enable/disable inputs and doing so, enable/disable their required attribute for model validation.

Everything worked great, until things changed in RC2.

You can read more about the decision to make the switch.

Asp.net MVC 2.0 Data Annotations Validation doesn’t emit correct JSON 1

Apparently I’m not the only one to have this sneaky little problem that has been driving me mad. Given the simple model:

public class Foo {
    [Required] public string Bar {get;set;}
}

And a simple view:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<Foo>" %>
<%@ Import Namespace="MvcApplication2.Models"%>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
	Index
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

<script src="/Scripts/jquery-1.3.2.js" type="text/javascript"></script>
<script src="/Scripts/jquery.validate.js" type="text/javascript"></script>
<script src="/Scripts/MicrosoftMvcJQueryValidation.js" type="text/javascript"></script>

 <h2>Index</h2>
 <%= Html.ValidationSummary() %>
 <% Html.EnableClientValidation(); %>
 <%
  using (Html.BeginForm())
  {
    ViewContext.FormContext.ClientValidationFunction = "EnableClientValidation"; %>
    <%= Html.LabelFor(x=>x.Bar) %>
    <%= Html.TextBoxFor(x=>x.Bar) %>
    <input type="submit" />
<%  } %>
</asp:Content>

You would expect the form to emit the JSON needed to pass to the jQuery validator right? WRONG! It just outputs the following

<script type="text/javascript">
//<![CDATA[
EnableClientValidation({"Fields":[],"FormId":"form0"}, null);
//]]>
</script>

That’s no help; however, when you give it the <%= Html.ValidationMessageFor(x=>x.Bar) %> you start to see what you expect.

There is no ViewData item of type ‘IEnumerable<SelectListItem>’ that has the key ‘myProperty’. 1

While figuring out the ASP.Net MVC 2.0 Preview 2 Expression HtmlHelpers (that’s a mouthful), I ran into this error a couple times. It didn’t make any sense to me because there shouldn’t be ViewData item with the key “MyProperty” with that type.

It turns out I was doing this:

<%= Html.DropDownListFor(x=>x.MyProperty, Model.AllMyPropertyListItems) %>

It throws this exception when AllMyPropertyListItems is null.

Testing when ModelState is invalid. 0

As a follow up to my previous post, testing when ModelState is relatively simple, but leaves me feelings a little uneasy about my tests.

Here is an example tests, stripped down to remove some of the unnecessary bits

public class When_submitting_lead_data_that_is_in_an_invalid_state : Specification
{
    // class fields...etc

    protected override void Given()
    {
        base.Given();
        _mockOrdersRepository = MockRepository.GenerateStub<IOrdersRepository>();
        _checkoutController = new CheckoutController(_mockOrdersRepository);

        _leadDataWithEmptyPlanName = new PreCheckoutViewData();

        // add an error to the model state
        _checkoutController.ModelState.AddModelError("", "");

        _exceptionWasThrown = false;
    }

    protected override void When()
    {
        base.When();
        try
        {
            _checkoutController.Checkout(_leadDataWithEmptyPlanName);
        } catch(ArgumentNullException)
        {
            _exceptionWasThrown = true;
        }
    }

    [Then] public void an_exception_is_thrown()
    {
        _exceptionWasThrown.ShouldBe(true);
    }
}

This feels pretty loose to me, but there can be another tests that tests which states the model will appear invalid.

ModelState.IsValid is always true 2

We ran into a snag with using the new default Data Annotations Validation in ASP.Net MVC 2.0 Preview 2. Maybe it isn’t a snag, but it is not the behavior I would have expected.

Take the following model:

public class Person
{
  public string ID { get; set; }

  public string FirstName { get; set; }
  public string LastName { get; set; }

  [Required (ErrorMessage = "Required")]
  public string EmailAddress { get; set; }
}

Pretty straight forward, but if you post this form:

    <form action="/person/edit" method="post">
        <input type=hidden name="id" id="id" value="" />
        <input type="text" id="firstname" name="firstname" /> 
        <input type="text" id="lastname" name="lastname" /> 
        <input type="submit" />
    </form> 

To this controller action:

[HttpPost] public virtual ActionResult Edit(Person data)
{
   if (!ModelState.IsValid) throw new ItBrokeException();
   return View();
}

Personally, I would expect every post of that form to show up as invalid, but infact, it is always true. Add the following input to the form and it works.

   <input type="text" id="emailaddress" name="emailaddress" /> <br/>

After that, the ModelState.IsValid returns false, like it should.

Running specific JavaScript after partial postback 7

I know how to run JavaScript after every postback, but on occasion I want to run some JS after a specific postback (i.e. show a flash message message that says the processing completed successfully). This hasn’t been as simple as I thought it should be. I even posted to StackOverflow to see if there was something I was missing or if it really is this messy. The only answer I’ve received to date suggested the method I already had in mind when posting the question, so I thought I would work out the full example and show you how it looks.

Background: First a little background. I have a UserControl that I use to display a nice status and/or error message on all my pages. It’s in the master page and has some really convenient JavaScript to show it. For all the ajaxy stuff we do it works great. It flashes in and fades out; however, we can’t show it on the partial postback events…yet. We need to run some JS on the client side after the postback is done to either show the error message or success message.

Overview: Basically, we are going to put a hidden field on the page and register an event handler to run after partial postback to see if there is any JavaScript in it, and execute there is. Pretty simple.

I created a user control to hold all the hidden fields and JS to register the postback events. It is going to get reused in a lot of places so it makes sense to make it reusable. It could be made into a server control, but I’ll forgo that for now.

There isn’t much to the user control.

<%@ Control Language="C#"
    AutoEventWireup ="true"
    CodeFile        ="DoAfterPostback.ascx.cs"
    Inherits        ="DoAfterPostback" %>        

<asp:HiddenField ID="DoAfterPostbackJavaScriptHiddenField" runat="server"
    EnableViewState="false" />

It’ll need some code to create a property to access the JavaScript we want to run and register the event handlers.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class DoAfterPostback : System.Web.UI.UserControl
{
    public string DoAfterPostbackJavaScript { get; set; }

    protected void Page_Load(object sender, EventArgs e)
    {
        Page.ClientScript.RegisterStartupScript(
            this.GetType(),
            "DoAfterPostbackJavaScriptEngine",
            @"
                var prm = Sys.WebForms.PageRequestManager.getInstance();
                prm.add_endRequest(function(s, e) {
                    $('[id$=_DoAfterPostbackJavaScriptHiddenField]').each(doPostbackJS);
                }); 

                function doPostbackJS(i){
                    eval(this.value);
                }

            ",true);
    }

    protected override void OnPreRender(EventArgs e)
    {
        base.OnPreRender(e);
        DoAfterPostbackJavaScriptHiddenField.Value = DoAfterPostbackJavaScript;
    }
}

We probably don’t need jQuery in there, but we’re already using it so I continued to use it here.

Now let’s get to using it. We’ll need a page for that.

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register src="DoAfterPostback.ascx" tagname="DoAfterPostback" tagprefix="uc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script type="text/javascript" src="jquery.js"></script>
    <script type="text/javascript">
        function showMsg(s) {
            alert(s);
        }
    </script>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
        <div>
            <asp:UpdatePanel ID="UpdatePanel1" runat="server">
                <ContentTemplate>
                    <uc1:DoAfterPostback ID="DoAfterPostback1" runat="server" />
                    <asp:TextBox ID="TextBox1" runat="server" />
                    <asp:Button ID="Button1" runat="server"
                        Text    ="Button"
                        OnClick ="ButtonClicked" /><br />
                    <asp:Label ID="Label1" runat="server"
                        Text    ="Label" />
                </ContentTemplate>
            </asp:UpdatePanel>
        </div>
    </form>
</body>
</html>

We have a textbox, button and a label inside an UpdatePanel. We also have our user control in there. This will let the hidden field value change on partial postback and the new value to show up in our event handler. In addition, there is a trivial JavaScript function on the page for us to call after the postback:showMsg . Technically we could just throw the alert right in our control, but this more closely mimics how we’re going to do for real.

Finally we need some code on the server side to put some JS in our hidden field.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class _Default : System.Web.UI.Page
{
    protected void ButtonClicked(object sender, EventArgs e)
    {
        var s = TextBox1.Text;
        if (string.IsNullOrEmpty(s)) return;

        Label1.Text = string.Format("The user says {0}.", s);
        DoAfterPostback1.DoAfterPostbackJavaScript = "showMsg('" + s + "');";
    }
}

That should be it. Now we can enter text, hit the button and see both our server side processing take place and the client side JavaScript execute after the postback as completed.

jQuery Validation and ASP.Net Web Forms 0

I’ve been dark for a while, but there has been a lot in the works. Hopefully there will be a flood of posts coming, most with a happier outcome than this one; however, to get back in the swing of things, I’ll take make it short and sweet.

I get really frustrated with ASP.Net Web Forms

We all know and love jQuery. It is an amazing JavaScript framework. It’s made it possible, for even the staunchest back-end developers, to get excited about writing UI. ASP.Net is the bane of jQuery enjoyment. I’ve covered this in jQuery Selectors and ASP.Net Controls. I have a new frustration: the giant HTML Form ASP.Net puts in the Web Form page.

There is an amazing jQuery validation plugin. It can do just about any validation you’ll need with minimal effort. If you want to validate a single form, it’s great; however, if you need to validate only a portion of the form, it’s a pain.

Since ASP.Net sticks its own <form> tag on the page by default, you’re pretty much screwed. The only option I’ve found so far is to go through each element, running a validation on the individual elements.

Here is an example of what I’m doing:

$().ready(function(){

	$("form").validate();	

	$("#firstname").rules("add", {
	 	minlength: 2,
		required:  true
	});

	$("#submit").click(function(){
		var valid = true;
		valid = valid && $("form").validate().element("#firstname");
		alert(valid);
   	});
});

I hope to find a better solution soon, but this will do for now.

Animating Partial Postbacks and UpdatePanels with jQuery 4

Update panels make it really easy to do pseudo ajaxish stuff with little or no extra effort. Wrap your controls in a an UpdatePanel, pay for the entire page to post back and go through its life-cycle and you’re pretty much there. I’ve already written a bit on the extra steps you’ll need if you’re doing some jQuery magic inside that UpdatePanel. It’s not difficult and easy enough to either delegate up the chain to something outside the UpdatePanel (e.g. event delegation) or rinse and repeat the jQuery after the partial post back is complete (e.g. add_endRequest).

What would be really slick-o-matic is if we could do some animation to transition the partial postback so it wouldn’t just explode with new content in our face. Now granted, that may be an overstatement. The content changes usually aren’t drastic or very distracting to but let’s add a bit of polish.

Keep in mind this is a first draft, done in about 30 minutes after an idea landed in my head at 3am. It still needs some polishing, but the major hurdles are taken care of.

For our example, we’ll have a simple Div that will be worked upon in a partial postback. Our goal will be to gently transition the display of that work to the user. Something like this.

Original Video – More videos at TinyPic

We’ll start off with some basic styles to help illustrate the change. We’ll change the content of the Div and also the class.

	#mydiv{ width:200px; border:solid 1px black; font-size:xx-large; }
	.myclass0 { height:200px; background-color:Blue; }
	.myclass1 { height:100px; background-color:Red; }
	.myclass2 { height:150px; background-color:Lime; }
	.myclass3 { height:50px; background-color:Purple; color:White; }

Nothing too exciting, but our partial postback will give us some new dimensions and style. It’ll get the point across.

Next we need some HTML.

<form id="form1" runat="server">
	<asp:ScriptManager ID="ScriptManager1" runat="server">
	</asp:ScriptManager>
	<div>
		Original time: <%= DateTime.Now.ToLongTimeString() %> 
		<asp:UpdatePanel ID="UpdatePanel1" runat="server">
			<ContentTemplate>
				<asp:Button runat="server" Text="Button" ID="MyButton"
						OnClick="MyButtonClick"
					OnClientClick="return ClientClick(this,'#mydiv'); " />
				<div id="mydiv" runat="server">
					<asp:Label runat="server" Text="Label"
                                           ID="MyLabel"></asp:Label>
				</div>
			</ContentTemplate>
		</asp:UpdatePanel>
	</div>
</form>

The magic is going to live in the ClientClick event handler, but forget about that for now. The server side OnClick event will do some trivial work.

 protected void MyButtonClick(object sender, EventArgs e)
 {
	 MyLabel.Text = DateTime.Now.ToLongTimeString();
	 var c = "myclass" + DateTime.Now.Millisecond % 4;
	 mydiv.Attributes.Add("class",c);
 }

This could just as easily toggled a view from edit mode to read-only, expanded a detailed area, etc. Here, we are just picking one of four css classese to use on the div and also giving a label the current time. Like I said this isn’t the cool part.

The final steps are to do the following:

  • Get the jQuery animation to happen on the click of the button.
  • Get the partial postback to wait until the animation is done.
  • Get the DOM to appear in the pre-animation state when it comes back from the partial postback.
  • Get the animation to happen once we have the new page ready to go.

The function needs to know what needs to be animated upon. I’ve been using slide transitions, but the animation is something I think can very easily be abstracted out. We make some quick tests to see if we are in the process of animating or if the animation has been completed. I struggled with how to delay the button click’s post back from happening until the animation is complete and finally decided to just let it happen a second time, but to flag the second click to allow it to continue posting back. I’m using the jQuery data feature to keep some state about the button.

If we are currently sliding, then there is nothing left to do, and if we are done sliding then we can skip the other steps and continue the postback.

if (d.data("isSliding")) return false;
if (d.data("doneSliding")) return true;

If we don’t meet those conditions, we need to slide. We set our flag, wire up the end request handler and the callback to the slide method and kick off the slide.

The slide method is the simplest.

d.slideUp(500,
	function() {
		d.data("isSliding", false);
		d.data("doneSliding", true);
		$(sender).click();
	});

Slide our control up, and when we’re done, set some flags and kick off the click a second time. With the flags set, we’ll fire off the postback. Nothing really fancy here either. Notice we can still reference d here. We don’t need to find it again.

The endRequest handler looks a bit more involved.

var prm = Sys.WebForms.PageRequestManager.getInstance();

var f = function(s, e) {
	d = $(whatToSlideDown);
	d.css("display", "none");
	d.slideUp(0);
	d.slideDown(500);
	prm.remove_endRequest(f);
}

prm.add_endRequest(f);

Here, inside the function we do need to ask jQuery to find what we want again. The DOM has been changed and the old d we created from $(whatToSlideUp) probably isn’t there anymore. In addition, we need to set the element in the state we left the old element. In this case, we want the display to be set to “none”. This allows the slideDown to work just like we would expect following a slideUp.

This is part of the setup that I don’t like. I’ve tried quickly running a slideUp(0) on the the new elements, but I was seeing a flash of the default state before getting the slide up. This is something that I would like to factor out. The animation, including this default state should be a parameter to the function.

Here is a look at the function as a whole. It’s a stupid function name I know, but that is easily changed.

function ClientClick(sender,whatToSlideUp,whatToSlideDown) {

	if (!whatToSlideDown)
		whatToSlideDown = whatToSlideUp;

	var d = $(whatToSlideUp);

	if (d.data("isSliding")) return false;
	if (d.data("doneSliding")) return true;

	d.data("isSliding", true);

	var prm = Sys.WebForms.PageRequestManager.getInstance();

	var f = function(s, e) {
		d = $(whatToSlideDown);
		d.css("display", "none");
		d.slideUp(0);
		d.slideDown(500);
		prm.remove_endRequest(f);
	}

	prm.add_endRequest(f);

	d.slideUp(500,
		function() {
			d.data("isSliding", false);
			d.data("doneSliding", true);
			$(sender).click();
		});
	return false;
}

That’s it. There is definitely room to polish this up and make it more flexible. I’ll post back when I get it up and running in a real application so it looks more useful.

Asp.Net and forcing password change after PasswordRecovery 0

Like many websites, we implemented a feature that will let our users self recover lost or forgottton passwords.  Asp.Net provides this right out of the box with the PasswordRecovery control.  It took us 90% of the way to where we want to be. It verifies the account exists, generates a random password and mails it to user.  The last step we wanted was to force the user to change their password after they’ve requested a new on from the PasswordRecovery control. Turns out, it wasn’t that bad.

We created a new profile property for our user to track if we were requiring a password change on the next login.

 <profile>
   <properties>
     <add name="RequirePasswordChange" type="System.Boolean" allowAnonymous="false"/>
   </properties>
 </profile>

We already had our ForgotPassword.aspx page with our PasswordRecovery control on it. We just needed to write up one more event.

<asp:PasswordRecovery ID="PasswordRecovery1" runat="server"
     OnSendingMail="SendingMail" >
 </asp:PasswordRecovery>

In the new event we’ll set that flag so we know the next time they successfully log in, we’ll make them change their password. I didn’t really see any other event that was more appropriate than SendingMail, but since we only send the e-mail when we pass all the challenges we’ve setup, it works well enough.

Also, since we’re not logged in, we have to ask for the profile by name, and then make the change.

protected void SendingMail(object sender, MailMessageEventArgs e)
{
    var p = Profile.GetProfile(PasswordRecovery1.UserName);
    p.RequirePasswordChange = true;
    p.Save();
}

The login page has some code added after we login.

<asp:Login ID="Login1" runat="server" OnLoggedIn="LoggedIn">
</asp:Login>

If we require the password change, then redirect to the new page.

protected void LoggedIn(object sender, EventArgs e)
{
    if (Profile.GetProfile(Login1.UserName).RequirePasswordChange)
        Response.Redirect("ChangePassword.aspx");
}

Lastly, ChangePassword.aspx resets the flag on successful login.

<asp:ChangePassword ID="ChangePassword1" runat="server"
    OnChangedPassword="ChangedPassword">
</asp:ChangePassword>
protected void ChangedPassword(object sender, EventArgs e)
{
    Profile.RequirePasswordChange = false;
    Profile.Save();
}

That’s it.

Rebasing Javascript via a MasterPage 1

I’ve been looking for a way to rebase the URL for the javascript we include in our master pages. We add a lot of javascript in the master pages such as the following.

    <link href="../css/ui-theme.css" rel="stylesheet" type="text/css" />
    <script language="javascript" type="text/javascript" src="../js/jquery-1.3.js" />

The problem is, as everyone probably knows, the relative path to the file is from the master page, not from the client page. For some reason, ASP.Net is kind enough to rebase the link tag’s href property, but not the javascript link.

After asking the Google, I saw a lot of examples to solve the problem like by using the ResolveClientUrl as follows:

    <script language="javascript" type="text/javascript"
        src='<%= ResolveClientUrl("~/js/jquery-1.3.js") %>' />
    <!-- or -->
    <script language="javascript" type="text/javascript"
        src='<%# ResolveClientUrl("~/js/jquery-1.3.js") %>' />

However, I saw no one suggest this, which I believe to be a little cleaner.

    <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true">
        <Scripts>
            <asp:ScriptReference Path="~/js/jquery-1.3.js" />
        </Scripts>
    </asp:ScriptManager>

Granted, this brings in some ASP.Net Ajax that may ugly things up, but it works for our own javascript files.

Next Page »