Javascript executing MVC code in If statement incorrectly

11,443

Solution 1

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.

Solution 2

Solution in a nutshell:

I tested your code and could say, there is no issue with your HW/SW update. I am running VS 2010 on Win 7. The solution could be Change your code for example this way

<script language="javascript" type="text/javascript">

            @{ // C# server side part to get the dateParameters
                var dateParameters = string.Empty;
                if (Model.Birthday.HasValue)
                {
                    dateParameters = "" + @Model.Birthday.Value.Year
                                 + ", " + (@Model.Birthday.Value.Month - 1)
                                 + ", " + @Model.Birthday.Value.Day;
                }
            }
            // JS client side, later working with just created dateParameters 
            var today = new Date(@dateParameters);
            alert(today);
    </script>

Detailed explanation:

to find the answer let split what is the snippet above doing. It is just rendering an HTML code. Let's see it step by step

I. HasValue

@Model.Birthday = new Date(2012,11,4)

1) Razor is sending to Response stream string until the first @ operator

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

if("

2) here Razor evaluates this part @Model.Birthday.HasValue as a boolean and converts it to string so the next part of a code would be

True

3) Append next direct string

"){
    var today = new Date("

4) evaluat the @Model.Birthday.Value.Year and inject its string value

2012

5) 6) 7) ... and so on the final result sent to Response is:

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

    if ("True"){
        var today = new Date("2012", "11" - 1, "4");
    } else{
        today = new Date();
    }
</script>

II. Birthdate is NULL

In case that the @Model.Birthday = null the first steps 1), 2) and 3) are the same:

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

    if ("True"){
        var today = new Date("

4) Exception Nullable object must have a value

Finally, there is a place where we get the Nullable exception.

@Model.Birthday.Value.Year

is called while

@Model.Birthday == null

Solution 3

The problem is that you're mixing up client-side code and server-side code. The javascript is run client-side, but only after all the server-side code has been completed.

So @Model.Birthday.HasValue, @Model.Birthday.Value.Year etc. will all be evaluated on the server-side, before it ever gets anywhere near the browser. The javascript code (and therefore in this case, the if statement) will never get to the browser as the exception is occurring server-side, trying to evaluate Model.Birthday.Value.Year on a null object.

You need to move the conditional server-side, e.g.

@if(Model.Birthday.HasValue){
    @:today = new Date(@(Model.Birthday.Value.Year), @(Model.Birthday.Value.Month) - 1, @(Model.Birthday.Value.Day));
} else {
    @:today = new Date();
}
Share:
11,443
Tyler Davey
Author by

Tyler Davey

Updated on June 17, 2022

Comments

  • Tyler Davey
    Tyler Davey almost 2 years

    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?