😨 Problem#
TL;DR: see Summary and the working code snippet if you want to try it right away.
In my company there is an old Web Form Website running .NET Framework 3.5. I migrated it to an ASP.NET Web Form Application running .NET Framework 4.7.2 few months ago. The website is running just like the old one but recently a user reported that:
a
<select>
list which should triggers an AJAX call and refresh part of the page does not work. Instead, the page is reloaded everytime when the item in the<select>
list is changed.
I used almost a day to figure out what’s (probably) happened and I hope that this article will be helpful to someone in the future who (still) encounter this problem.
Expected vs Actual Behavior#
There is a <form>
containing a <select>
list (generated by <asp:DropDownList>
). When the selected option is changed, an AJAX call will be fired and new items will be fetched. Besides, there is a text area for user to input remarks. A submit button will submit the form.
Expected#
The page will not be reloaded when the selected option is changed. The content in the text area retains.
Actual#
Once the selected option is changed, a form POST is fired (instead of an AJAX call). The content in the text area is therefore being cleaned.
This is related to AJAX calls so the first thing I checked was how the AJAX call was initiated.
😀 Solution (Part 1): Updating AjaxControlToolkit
#
In the .aspx
file I saw a <asp:UpdatePanel>
and a <asp:DropDownList>
. This is the simplified structure of that part:
<asp:UpdatePanel runat="server" ID="UpdatePanel2" UpdateMode="Conditional" ChildrenAsTriggers="true">
<ContentTemplate>
<asp:DropDownList runat="server" ID="ddItemTypes" AutoPostBack="true" OnSelectedIndexChanged="ddItemTypes_OnSelectedIndexChanged" />
</ContentTemplate>
</asp:UpdatePanel>
AutoPostBack
is there so the app should use AjaxControlToolkit
to make AJAX calls (I guess that’s the standard right?).
Nuget Package Update#
I checked the package.config
:
<package id="AjaxControlToolkit" version="4.1.50508" targetFramework="net35" />
Looks like the targetFramework
is not right. Maybe it’s time to update the package too.
<package id="AjaxControlToolkit" version="20.1.0" targetFramework="net472" />
Some Breaking Changes#
AjaxControlToolkit has some breaking changes since version 15+. In short, you need to:
Uninstall the old version and install the new version of
AjaxControlToolkit
.Change
<asp:ToolkitScriptManager>
to<asp:ScriptManager>
Remove some unused configs in
web.config
(see this)[Optional] Install
AjaxControlToolkit.HtmlEditor.Sanitizer
Change the namespace
AjaxControlToolkit.HTMLEditor
toAjaxControlToolkit.HtmlEditor
Change the namespace
AjaxControlToolkit.HTMLEditor.ToolbarButton
toAjaxControlToolkit.HtmlEditor.ToolbarButtons
You may also read the official complete migration guide.
After I upgrade the Nuget package, the AJAX calls was working partially: It only works on Chromium-base browsers like Chrome and Edge. However, it does not work on Firefox (85.0) and Safari (14.0.1).
😐 😐 😐
Different __doPostBack()
in Firefox?#
This is what I got from Firefox’s dev tool:
The initiator is from __doPostBack()
in the .aspx
page. In Chrome, it’s from ScriptResource.axd
. Below is the expected call stack: the call is an XHR request fired from ScriptResource.axd
.
Looks like the stack items from bottom to _doPostBack()
are the same. I therefore dug into _doPostBack()
and eventually found this piece of code in ScriptResource.axd
:
if (!this._postBackSettings.async) {
form.onsubmit = this._onsubmit;
this._originalDoPostBack(eventTarget, eventArgument);
form.onsubmit = null;
return;
}
if this._postBackSettings.async
is false
, _originaldoPostBack()
will be called and that is the __doPostBack()
function defined in the .aspx
page. This will trigger a page reload instead of an AJAX call.
Furthermore, I found that the asyncTarget
in _postBackSettings
is null
but in Chrome that target is the <select>
list.
In Firefox (v85.0) the javascript event initiated is somehow keep propagating to a higher level than the
<select>
list (probably to the form’s level) and therefore being treated as a form POST instead of an AJAX call.
😀 Solution (Part 2): Adding <asp:AsyncPostBackTrigger>
#
I did some research related to the Firefox-specific page reload and seems none of them were talking about the issue I met. I tried to study the fundamentals of this PostBack behaviour.
Finally I got this article Avoid (Prevent) Page refresh (PostBack) after SelectedIndexChanged is fired in ASP.Net DropDownList. It suggested what I might missing is an asp:AsyncPostBackTrigger
.
Also, this StackOverflow answer menitoned the asp:AsyncPostBackTrigger
thing.
So my understanding is:
When using the
ScriptManager
(i.e. the one we introduced when updating theAjaxControlToolkit
), we need to define anasp:AsyncPostBackTrigger
Trigger
in order to make the call AJAX.
Therefore, I added a Trigger
section to the .aspx
file:
<Triggers>
<asp:AsyncPostBackTrigger ControlID="ddItemTypes" EventName="SelectedIndexChanged" />
</Triggers>
This Trigger
section should be placed under the same <asp:UpdatePanel>
with the <asp:DropDownList>
. Here is the complete snippet:
The Working Code Snippet#
<asp:UpdatePanel runat="server" ID="UpdatePanel2" UpdateMode="Conditional" ChildrenAsTriggers="true">
<ContentTemplate>
<asp:DropDownList runat="server" ID="ddItemTypes" AutoPostBack="true" OnSelectedIndexChanged="ddItemTypes_OnSelectedIndexChanged" />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="ddItemTypes" EventName="SelectedIndexChanged" />
</Triggers>
</asp:UpdatePanel>
Note:
EventName
is optional here but since I just want this trigger to listen theSelectedIndexChanged
event, I add them all.The
ControlID
has to be an exact match with theID
of<asp:DropDownList>
.
After I added the <asp:AsyncPostBackTrigger>
, all the browsers including Chrome, Edge, Firefox (85.0) and Safari (14.0.1) are working.
Summary#
When your .NET 4.5+ ASP.NET Web Form Application using AjaxControlToolkit
does not work as expected and cause the controls like <asp:DropDownList>
cannot fire an AJAX call (but triggers a form POST), make sure that:
The
AjaxControlToolkit
Nuget package is up to date (and did the proper migration steps here).The
<asp:UpdatePanel>
section should contain a<Triggers>
section. In that section there is an<asp:AsyncPostBackTrigger>
withControlID
same as theID
of your control (which is also placed under the same UpdatePanel section).