Search code examples
javascriptasp.net-mvcinternet-explorer-10

Javascript executing MVC code in If statement incorrectly


I am a bit stumped with the following code. It worked fine in VS2010 on Windows 7, and now that I've upgraded the hardware to Windows 8 and VS 2012, it does not.

I have the following JavaScript code in my MVC application:

<script language="javascript" type="text/javascript">
var today;

if("@Model.Birthday.HasValue"){
    var today = new Date("@Model.Birthday.Value.Year", "@Model.Birthday.Value.Month" - 1, "@Model.Birthday.Value.Day");
}else{
    today = new Date();
}

The Model is pulling from a ViewModel that has a property that looks like this:

    public System.DateTime? Birthday
    {
        get { return user.Birthday; }
        set { user.Birthday = value; }
    }

The exception is thrown in the line:

var today = new Date("@Model.Birthday.Value.Year", "@Model.Birthday.Value.Month" - 1, "@Model.Birthday.Value.Day");

It comes back with "Nullable object must have value", which it should if it jumps in that line to execute. But, my if statement is returning false. If I remove the line and just put in an alert statement, it never executes that line or shows the alert, because the Birthday property is null. Something seems to have shifted in how JS executes MVC code, and it seems to evaluate the entirety of the code block, regardless of the if statement. I've also tried the following, but to no avail:

  • Tested in Chrome for W8
  • Tested in a try / catch block. Interestingly, it doesn't catch the exception (I guess JS won't catch .NET exceptions)
  • Tested by explicitly stating @Model.Birthday.HasValue == true
  • Tested by changing it to @Model.Birthday.Value != null
  • Tested in Release mode over Debug Mode

This should be simple - test to see if a value is null, and if it's not, create a JS Date object from the .NET property, if it is, create a new JS Date object, but for some reason, it want's to evaluate and execute that line, even though HasValue is false.

Really stumped. Has anyone seen something like this?


Solution

  • I can see a couple of things going on here that are impacting your ability to render the correct data.

    First, the MVC variables are processed on the server side. Your current implementation is always trying to render a value for the @Model.Birthday.Value properties in your Javascript in order to generate the HTML. This will happen regardless if the property is true or false in your current example since it is relying on the client-side Javascript to do the conditional and not Razor. Below is a complete working example of your issue.

    View Model

    namespace MvcApplication1.Models
    {
        public class TestViewModel
        {
            public DateTime? NullDate { get; set; }
    
            public DateTime? DateWithValue { get; set; }
        }
    }
    

    Controller

    public ActionResult Index()
            {
                return View(new TestViewModel{DateWithValue = DateTime.Now, 
    NullDate = null});
                }
    

    View

    @model MvcApplication1.Models.TestViewModel
    @{
        ViewBag.Title = "Index";
    }
    
    <h2>Index</h2>
    
    <script type="text/javascript">
        var today;
        var anotherDay;
    
        @{if(Model.NullDate.HasValue)
        {
            @: alert('Null date had a value!');
            @: today = new Date('@Model.NullDate.Value.Year', parseInt('@Model.DateWithValue.Value.Month') - 1, '@Model.NullDate.Value.Day');
        }else
          {
            @: alert('Null date did not have a value');
            @: today = new Date();
        }}
    
        @{if (Model.DateWithValue.HasValue)
          {
            @: alert('DateWithValue had a value!');
                @: anotherDay = new Date('@Model.DateWithValue.Value.Year', parseInt('@Model.DateWithValue.Value.Month') - 1, '@Model.DateWithValue.Value.Day');
              @: alert(anotherDay);
          }
          else
          {
              @: alert('DateWithValue date did not have a value');
            @: anotherDay = new Date();
        }}
    </script>
    

    Now obviously, the code is duplicated in the Javascript section in order to show it works for both Null and Non-null values in the same controller action.

    As you can see in the View, we are ensuring that we are using the Razor syntax for the checking of the Model and Javascript for setting the actual client-side variable. Also, I wonder if your '- 1' was working properly. In order to do the subtraction in this example, I had to cast that value to an Int from the Javascript, but this is a minor detail.

    For reference, this is an MVC 4 project on VS2012, Windows 8 Professional tested with Chrome.