B. Clay Shannon B. Clay Shannon - 2 months ago 8
Ajax Question

Why does my dynamically added (back) jQuery handler fail?

I am replacing the html on a page when a button is clicked - everything, including the css and jQuery. The jQuery (button click event handler) works the first time, but not thereafter; in fact, it then throws an exception.

This static ajax jquery callback code contains this:

url: '@Url.RouteUrl(routeName: "QuadrantData", routeValues: new { httpRoute = true, unit = "un", begdate = "bd", enddate = "ed" })'
.replace("un", encodeURIComponent(_unit))
.replace("bd", encodeURIComponent(_begdate))
.replace("ed", encodeURIComponent(_enddate)),


...which gets transformed into this at runtime:

url: '/api/un/bd/ed'
.replace("un", encodeURIComponent(_unit))
.replace("bd", encodeURIComponent(_begdate))
.replace("ed", encodeURIComponent(_enddate)),


This code is part of an ajax call in a button's event handler. As mentioned, it works perfectly the first time the button is clicked, but after the html (and css and jquery) is replaced in response to that click, I get the following:

HTTP Error 404.0 - Not Found
The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.

Most likely causes:
The directory or file specified does not exist on the Web server.
The URL contains a typographical error.
A custom filter or module, such as URLScan, restricts access to the file.

Things you can try:
Create the content on the Web server.
Review the browser URL.
Check the failed request tracing log and see which module is calling SetStatus. For more information, click here.

Detailed Error Information:
Module IIS Web Core
Notification MapRequestHandler
Handler StaticFile
Error Code 0x80070002
Requested URL http://localhost:52194/@Url.RouteUrl(routeName: "QuadrantData", routeValues: new { httpRoute =
true, Abuelosit = "un", begdate = "2016-08-28", enddate = "2016-08-31" })?_=1473366761632
Physical Path C:\Projects\ProActWebReports\ProActWebReports\@Url.RouteUrl(routeName: "QuadrantData", routeValues:
new { httpRoute = true, Abuelosit = "un", begdate = "2016-08-28", enddate = "2016-08-31" })

Logon Method Anonymous
Logon User Anonymous
Request Tracing Directory C:\Users\cshannon\Documents\IISExpress\TraceLogFiles\PROACTWEBREPORTS

More Information:
This error means that the file or directory does not exist on the server. Create the file or directory and try the request again.


What resource has been removed? Which "file or directory does not exist on the server"? Actually, what looks very fishy in that exception message is this:

*Abuelosit = "un", begdate = "2016-08-28", enddate = "2016-08-31"*


It should be:

*unit = "Abuelos", begdate = "2016-08-28", enddate = "2016-08-31"*


...so it seems that "Abuelos" and "unit" are getting mangled/jumbled up somehow - Abuelosit = "un" should be unit = "Abuelos". If this is the problem, how can I rectify it?

BTW, the REST method that is called (the first time the button is clicked) is:

[HttpGet]
[Route("{unit}/{begdate}/{enddate}", Name = "QuadrantData")]
public HttpResponseMessage GetQuadrantData(string unit, string begdate,
string enddate)
{
_unit = unit;
_beginDate = begdate;
_endDate = enddate;
string beginningHtml = GetBeginningHTML();
string bodyBeginningHtml = GetBodyBeginHTML();
string top10ItemsPurchasedHtml = GetTop10ItemsPurchasedHTML();
string pricingExceptionsHtml = GetPricingExceptionsHTML();
string forecastedSpendHtml = GetForecastedSpendHTML();
string deliveryPerformanceHtml = GetDeliveryPerformanceHTML();
string endingHtml = GetEndingHTML();
String HtmlToDisplay = string.Format("{0}{1}{2}{3}{4}{5}{6}",
beginningHtml,
bodyBeginningHtml,
top10ItemsPurchasedHtml,
pricingExceptionsHtml,
forecastedSpendHtml,
deliveryPerformanceHtml,
endingHtml);

return new HttpResponseMessage()
{
Content = new StringContent(
HtmlToDisplay,
Encoding.UTF8,
"text/html"
)
};
}


UPDATE



For posterity, here is the old code (commented out) and the new, from Ricardo, that works:

First, in Index.cshtml (the raw html):

//$("#btnGetData").click(function () {
$("body").on( "click", "#btnGetData", function() {


And in the Controller code, where I'm dynamically generating html to replace the previous:

//builder.Append("$(\"#btnGetData\").click(function () {");
builder.Append("$(\"body\").on( \"click\", \"#btnGetData\", function() {");


This works perfectly now.

Answer

From my experience, if an AJAX request works fine the first time and fails the second time and you are replacing the original HTML with new HTML from the AJAX request, it means that the original click handlers are gone.

If you are attaching click handlers to the button like this:

$("button.someAction").click(function() {
    $.ajax("/api/a/b/c");
    // etc.
});

they will be gone once the AJAX request completes. When you click the button which no longer has an event handler it tries to submit something to the server and you get a weird URL.

You should be assigning them with .on() instead of click(). The .on() should be assigned to the container whose insides are replaced in the new request like this.

$("#container").on( "click", "button.someAction", function() {
  $.ajax("/api/a/b/c");
});

This way the action on the button will be maintained through all requests.

Hope it helps.