Search code examples
javascriptc#jqueryasp.net-mvchtml.actionlink

Why is my routeValues variable changing between my view loading and me clicking on my Html.ActionLink in ASP.NET MVC?


I have an ASP.NET MVC application where I have a textbox with a jquery datePicker and a HTML.ActionLink that will download an Excel document that uses the date string picked from the datePicker. I want the link to the Excel download to use this date string as a parameter in its query string.

In the example code I will provide sample names for my classes and variables.

Here is an excerpt from my main view (View is named "TestView", Model is of class "TestModel"):

        @using (Ajax.BeginForm("TestForm", FormMethod.Post,
        new AjaxOptions
        {
            UpdateTargetId = "IdSomething",
            OnFailure = "handleFailure(xhr, status, 'IdSomething')"
        }))
    {
        @Html.AntiForgeryToken()
        @Html.Action("TestSettings", Model)
        <div class="clear margin-bottom-adjusted">
            @Html.ActionLink(Markup.Download_As_Microsoft_Excel, "Download",
            new { InstantlyUpdatedDate = Model.InstantlyUpdatedDate },
            new { @class = "download-excel" });
        </div>
    }

Here is the relevant excerpt from the "TestSettings" view(it also uses a model of the class "TestModel"). The submit button here and the variable "NotInstantlyUpdatedDate" are used for updating a graph, but this should not be relevant for the Excel download:

<div>
    @Html.TextBoxFor(m => m.NotInstantlyUpdatedDate, new { @class = 
    "datepicker", @onchange = "setExcelDownloadDate(" + @Json.Encode(Model) + 
    ", $(this))" })
    <input type="submit" value="@Markup.Update" class="btn" />
</div>

The "setExcelDownloadDate" function is defined in javascript like this:

function setExcelDOwnloadDate(model, item) {
    if (item.attr('name') === "NotInstantlyUpdatedDate")
    model.InstantlyUpdatedDate = item.val();

$.ajax({
    url: '../Test/UpdateTestView',
    type: 'post',
    data: {
        model: model
    }
});

}

Relevant excerpt from my Controller:

public TestController

{

    //Should only get called once by another view
    [HttpPost]
    public ActionResult InitTestView(TestModel model)
    {
         model.NotInstantlyUpdatedDate = "2018, 01, 01";
         model.InstantlyUpdatedDate = model.NotInstantlyUpdatedDate;
         return PartialView("TestView", model);
    }
    [HttpPost]
    public ActionResult UpdateTestView(TestModel model)
    {
        return PartialView("TestView", model);
    }

    [HttpPost]
    public Task<ActionResult> Download(DownloadModel model)
    {
       //download the model here
    }
}

Here is my "TestModel" class for this example:

[Serializable]
public class TestModel
{
    public string NotInstantlyUpdatedDate { get; set; }
    public string InstantlyUpdatedDate { get; set; }
}

And here is my DownloadModel class:

public class DownloadModel
{
    public string InstantlyUpdatedDate { get; set; }
}

OK, thank you for bearing with me. Here is what's happening:

First the InitTestView method is called, and it renders a TestView. If I place a breakpoint at the @Html.ActionLink line in the TestView, it will show me that the model.InstantlyUpdatedDate variable is "2018, 01, 01" (this is correct and the expected behaviour).

Since the TestSettings view is embedded in the TestView, it will render the Html.TextBoxFor for my datepicker. If I now inspect the Download button in my browser, a correct download query string will show, with the date "2018, 01, 01" as a parameter.

Now, let's say I pick the date ("2018, 01, 02") from the datepicker (the conversion to a date string is done in jquery, don't worry about this as it's working as expected). This date will now show in the textbox, and the @onchange event will trigger my javascript function setExcelDownloadDate. In this method, I can put breakpoints and see that my model.InstantlyUpdatedDate has indeed been set to "2018, 01, 02". This is the correct behaviour.

From the javascript function the ajax call sends the model object to my TestController. If I break in the function UpdateTestView, I can look at my model variable and also see here that the value has changed to "2018, 01, 02". Working correctly.

Now that this method returns a new instance of my TestView with the updated model, I can still break at the Html.ActionLink line and see that yes indeed, the Model.InstantlyUpdatedDate is "2018, 01, 02", which is correct.

However, here comes the problem. If i inspect the link in my browser, I will see that the url is incorrect. The date is not "2018, 01, 02", but still "2018, 01, 01". If I click the link and put a breakpoint in my Download method, the model's InstantlyUpdatedDate property will also be "2018, 01, 01" instead of "2018, 01, 02".

For some reason the model property InstantlyUpdatedDate seems to change back to it's original value. I do not know why this happens, and therefore I ask if some of you may be able to help me. This is part of a larger codebase, and something I don't know about might of course screw with what's happening. It could also be that this is the expected behaviour for some reason, and that I'm just not familiar enough with how this should work.

Thank you for your time.


Solution

  • I have a bit of trouble following this but I'll give a try. It seems like you aren't doing anything with the result of $.ajax. It will return the partial view with everything filled up but nothing is done with it.

    $.ajax({
        url: '../Test/UpdateTestView',
        type: 'post',
        data: {
            model: model
        }
    }).done(function( html ) {
        // $( "#results" ).append( html ); // Put the html somewhere
      });
    

    Personally, in the onchange, I would just update the link instead of the whole partial view. I usually update the partial view when there's a bit more changes than just a link.

    $('#linkId').attr('href', '@Url.Action(Markup.Download_As_Microsoft_Excel, "Download")?InstantlyUpdatedDate=' + item.val());