A.W. A.W. - 4 months ago 12
Javascript Question

MVC Razor $.Ajax not working well with each other

I'm doing maintenance on an MVC/Razor app and am trying to understand how some code works. There is a refresh button on the web page, and when the user clicks it, it's supposed to refresh a list of tasks. It seems to be broken, but in trying to trace how it works, I've run into what looks like a conflict between the way Ajax does things and the way MVC does partial views. I sort of suspect the code was originally written for the Ajax call but when they later decided to go MVC, they just overrode that with the partial view approach; but I'm not knowledgeable enough about how this works to be certain.

Here is the partial view (named "_TasksAndAlerts.cshtml") that contains a "Refresh" button --

@model TasksAndAlertsModel
<section class="block-1-2 pull-right" id="TasksAndAlertsSection">
<div class="inline">
<h3>tasks/alerts<span>@Model.alerts.Count</span></h3>
</div>
<div class="inline" style="margin: 19px 0px 0px 0px;">
<!-- this is the "refresh" button, visually it's an icon -->
<button style="width: 20px; height: 20px;" id="refreshTasksAndAlerts" class="k-button"
onclick="RefreshTasksAndAlertsAjaxCall(0);return false;">
<span style="position: relative; left: -6px; top: -6px;" class="k-icon k-si-refresh"></span>
</button>
</div>
<ul class="tasks" style="overflow: auto; height: 404px">
@for (int a = 0; a < Model.lockedByCurrentUserAlerts.Count; a++)
{
@Html.Raw(HttpUtility.HtmlDecode(Model.alerts[Model.lockedByCurrentUserAlerts.ElementAt(a)].ToString()));
}
@for (int b = 0; b < Model.unassignedAlerts.Count; b++)
{
@Html.Raw(HttpUtility.HtmlDecode(Model.alerts[Model.unassignedAlerts.ElementAt(b)].ToString()));
}
@for (int c = 0; c < Model.lockedByAnotherUserAlerts.Count; c++)
{
@Html.Raw(HttpUtility.HtmlDecode(Model.alerts[Model.lockedByAnotherUserAlerts.ElementAt(c)].ToString()));
}
</ul>
</section>


Here is the javaScript that is triggered by clicking the "Refresh"

function RefreshTasksAndAlertsAjaxCall(alertID) {
$.ajax({
type: "GET",
url: siteRoot + "Home/RefreshTasksAndAlerts/?t=" + Math.random(),
success: function (response) {
$(document.getElementById("TasksAndAlertsSection")).html(response);
},
error: function (response) {
//alert(response.responseText);
}
});
}


From reading about $.ajax, I got the impression that the contents of the success value would replace everything within the "getElementById("TasksAndAlertsSection")" part. But what is returned from "RefreshTasksAndAlerts" is a list, not HTML. And while it would be possible to process the list with some sort of javaScript, I don't see anything to make that happen.

Here is the controller code called from the javaScript

public PartialViewResult RefreshTasksAndAlerts(int? id)
{
LIMDUEntities db = closedKeyEntity;
return PartialView("HomePage/_TasksAndAlerts", limduDataHelper.GetTasksAndAlertsModel(HOURS_UNTIL_TASKS_SHOULD_BE_REASSIGNED, db));
}


The code referred to in the 'return' is a long piece of code that eventually returns a list (I'm just showing a piece of it, since what it builds isn't my question):

public TasksAndAlertsModel GetTasksAndAlertsModel(int HOURS_UNTIL_TASKS_SHOULD_BE_REASSIGNED, LIMDUEntities db)
{
ArrayList alerts = new ArrayList();
TasksAndAlertsModel TAAM = new TasksAndAlertsModel();
TAAM.alerts = alerts;
TAAM.unassignedAlerts = UnassignedIndexes;
TAAM.lockedByAnotherUserAlerts = lockedByAnotherUserIndexes;
TAAM.lockedByCurrentUserAlerts = lockedByCurrentUserIndexes;
return TAAM;
}


I'm confused by what actually happens -- it looks to me like the two approaches conflict with each other. Since the data returned to $.Ajax isn't HTML, I'm guessing that it just gets lost and the partial view returned from the controller is what gets displayed. If that's so, is the "success:" part even needed? Would there be a cleaner way to express this?

Answer

There is no conflict between MVC Razor and $.Ajax in this case. Your controller action RefreshTasksAndAlerts renders a partial view (a part of a page if you will) and returns only HTML code (no lists of any kind). In your case something like this is returned:

<section class="block-1-2 pull-right" id="TasksAndAlertsSection">
    <div class="inline"> 
        .... other html here ....
    </div>
</section>

JavaScript function RefreshTasksAndAlertsAjaxCall invokes/calls the controller action. When the response is returned (the above HTML code from the controller), success callback function is executed. That function just replaces the HTML within the element with id="TasksAndAlertsSection". In your case it is the actual <section> element that was rendered the first time page was loaded. So after the first click on "refresh" the html on the page would look like:

<section class="block-1-2 pull-right" id="TasksAndAlertsSection">
    <section class="block-1-2 pull-right" id="TasksAndAlertsSection">
        <div class="inline"> 
            .... other html here ....
        </div>
    </section>
</section>

Now, this is a bit of a problem since you now have two elements on the page with the same ID, which is invalid, although the browsers tolerate this and will render the page as you'd expect, more-less. Your JavaScript code might have problems with this situation.

The correction would be to enclose the original <section> element into a <div> element in the original view which renders the page ((!) not in the partial view). So, in your main view you'd have something like this:

<div id="SectionParent">
    .... render partial view the same way you are doing it now ....
</div>

Which will produce the html output like this:

<div id="SectionParent">
    <section class="block-1-2 pull-right" id="TasksAndAlertsSection">
        <div class="inline"> 
            .... other html here ....
        </div>
    </section>
</div>

Update the success callback to:

success: function (response) {
        $(document.getElementById("SectionParent")).html(response);
},

and it should all be good.

You can use dev tools in your browser and inspect the page html and the network traffic to confirm what is being returned from the partial view action.

Comments