ASP.NET 4.5 ScriptManager Improvements in WebForms

The ScriptManger control has undergone some key targeted changes in ASP.NET 4.5 which makes it easier to register, manage and combine scripts using the ASP.NET weboptimization feature. This post will highlight the changes that have happened to this control

Easy Integration with JQuery and JQueryUI

The default templates for WebForms ship with the following packages “ AspNet.ScriptManager.jQuery ” and “ AspNet.ScriptManager.jQuery.UI.Combined ”. These packages make it easier to bring in jquery and jqueryUI libraries  and also register them with the ScriptManager. Here is how it works.

These packages add the following ScriptMappings for jquery and jqueryUI in the PreApplicationStart method of the application

JQuery

 string str = "1.7.1";
     ScriptManager.ScriptResourceMapping.AddDefinition("jquery", new ScriptResourceDefinition
     {
         Path = "~/Scripts/jquery-" + str + ".min.js", 
         DebugPath = "~/Scripts/jquery-" + str + ".js", 
         CdnPath = "https://ajax.aspnetcdn.com/ajax/jQuery/jquery-" + str + ".min.js", 
         CdnDebugPath = "https://ajax.aspnetcdn.com/ajax/jQuery/jquery-" + str + ".js", 
         CdnSupportsSecureConnection = true, 
         LoadSuccessExpression = "window.jQuery"
     });

JQuery.UI

 string str = "1.8.20";
     ScriptManager.ScriptResourceMapping.AddDefinition("jquery.ui.combined", new ScriptResourceDefinition
     {
         Path = "~/Scripts/jquery-ui-" + str + ".min.js", 
         DebugPath = "~/Scripts/jquery-ui-" + str + ".js", 
         CdnPath = "https://ajax.aspnetcdn.com/ajax/jquery.ui/" + str + "/jquery-ui.min.js", 
         CdnDebugPath = "https://ajax.aspnetcdn.com/ajax/jquery.ui/" + str + "/jquery-ui.js", 
         CdnSupportsSecureConnection = true
     });

 

These SciptMappings are registered with ScriptManager as follows

 <asp:ScriptManager runat="server">
         <Scripts>
             <asp:ScriptReference Name="jquery" />
             <asp:ScriptReference Name="jquery.ui.combined" />            
         </Scripts>
     </asp:ScriptManager>

 

Now you can enjoy all the benefits of having ScriptManager such as

Debug/Release Support

If you are debugging your application(debug=true) in web.config then ScriptManager will serve the scripts from debug path(non minified scripts) such as “~/Scripts/jquery-1.7.1.js”

CDN Support

If your set EnableCDN=true on the ScriptManager control, then all the Scripts will be served from the CDN path such as “https://ajax.aspnetcdn.com/ajax/jquery-1.7.1.js

Override Script Mappings

Let’s assume that you wanted to override the scriptmappings to change the CDN path where these scripts are served from. You can do so by changing the scriptmapping before the page is accessed. Typically doing this in Global.asax is better

 string str = "1.7.1";
     ScriptManager.ScriptResourceMapping.AddDefinition("jquery", new ScriptResourceDefinition
     {
         Path = "~/Scripts/jquery-" + str + ".min.js", 
         DebugPath = "~/Scripts/jquery-" + str + ".js", 
         CdnPath = "https://https://code.jquery.com/jquery-"+ str + ".min.js", 
         CdnDebugPath = "https://code.jquery.com/jquery-"+ str + ".js", 
         CdnSupportsSecureConnection = true
     });

 

Updating Jquery/JqueryUI libraries

Let’s say a new version of Jquery or JqueryUI comes along. Traditionally you would have to download the jquery packages for these libraries and then update the script references everywhere on your pages. With the above integration of AspNet.ScriptManager.Jquery packges this scenario really becomes easy since when you update Jquery/JqueryUI you will get corresponding versions of AspNet.ScriptManager.Jquery/AspNet.ScriptManager.Jquery.UI which will update the scriptmappings to the current version of jquery that was downloaded and you do not have to change anything in your application

LoadSuccessExpression

This is a new property which is added to the ScriptMapping and it takes care of the following use case. Imagine that you are serving your scripts from a CDN path and if there is an outage in the CDN, your site will be affected because you will not be able to serve any scripts. This property takes in an expression which evaluates whether the script was loaded correctly and if it fails then it renders the script from the local application path.

Following is how the script tag looks like for jquery(assuming that you are serving scripts from CDN)

 <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.1.js" type="text/javascript"></script>
 <script type="text/javascript">
 //<![CDATA[
 (window.jQuery)||document.write('<script type="text/javascript" src="Scripts/jquery-1.8.1.js"><\/script>');//]]>
 </script>

 

Remapping Framework scripts

One neat improvement that happened in ASP.NET 4.5 is the decoupling of the “Microsoft Ajax script files(MicrosoftAjaxCore etc)” and the WebForms scripts(GridView.js etc). We did the work in this release so you can serve these scripts from your application Scripts folder rather than load then up from System.Web This makes the scripts easily redistributable and updateable. The following section explains on how you can use these scripts and combine them with WebOptimization feature

Serving Framework Scripts from inside the application

We created the following 2 packages “Microsoft.AspNet.ScriptManager.MSAjax” “Microsoft.AspNet.ScriptManager.WebForms” which do the following

Install the Scripts into your application so it looks like follows

fxscripts

These packages also create the following scriptmappings so that they can be registered with ScriptManager as follows

MicrosoftAjax scripts

We are registering all the scripts and we are bundling them together as “MsAjaxBundle”

 ScriptManager.ScriptResourceMapping.AddDefinition("MsAjaxBundle", new ScriptResourceDefinition
     {
         Path = "~/bundles/MsAjaxJs", 
         CdnPath = "https://ajax.aspnetcdn.com/ajax/4.5/6/MsAjaxBundle.js", 
         LoadSuccessExpression = "window.Sys", 
         CdnSupportsSecureConnection = true
     });
     PreApplicationStartCode.AddMsAjaxMapping("MicrosoftAjax.js", "window.Sys && Sys._Application && Sys.Observer");
     PreApplicationStartCode.AddMsAjaxMapping("MicrosoftAjaxCore.js", "window.Type && Sys.Observer");
     PreApplicationStartCode.AddMsAjaxMapping("MicrosoftAjaxGlobalization.js", "window.Sys && Sys.CultureInfo");
     PreApplicationStartCode.AddMsAjaxMapping("MicrosoftAjaxSerialization.js", "window.Sys && Sys.Serialization");
     PreApplicationStartCode.AddMsAjaxMapping("MicrosoftAjaxComponentModel.js", "window.Sys && Sys.CommandEventArgs");
     PreApplicationStartCode.AddMsAjaxMapping("MicrosoftAjaxNetwork.js", "window.Sys && Sys.Net && Sys.Net.WebRequestExecutor");
     PreApplicationStartCode.AddMsAjaxMapping("MicrosoftAjaxHistory.js", "window.Sys && Sys.HistoryEventArgs");
     PreApplicationStartCode.AddMsAjaxMapping("MicrosoftAjaxWebServices.js", "window.Sys && Sys.Net && Sys.Net.WebServiceProxy");
     PreApplicationStartCode.AddMsAjaxMapping("MicrosoftAjaxTimer.js", "window.Sys && Sys.UI && Sys.UI._Timer");
     PreApplicationStartCode.AddMsAjaxMapping("MicrosoftAjaxWebForms.js", "window.Sys && Sys.WebForms");
     PreApplicationStartCode.AddMsAjaxMapping("MicrosoftAjaxApplicationServices.js", "window.Sys && Sys.Services");

Here is the Bundle definition for these scripts

 bundles.Add(new ScriptBundle("~/bundles/MsAjaxJs").Include(
                 "~/Scripts/WebForms/MsAjax/MicrosoftAjax.js",
                 "~/Scripts/WebForms/MsAjax/MicrosoftAjaxApplicationServices.js",
                 "~/Scripts/WebForms/MsAjax/MicrosoftAjaxTimer.js",
                 "~/Scripts/WebForms/MsAjax/MicrosoftAjaxWebForms.js"));

 

Here is the ScriptManger registration for these scripts

 <asp:ScriptManager runat="server" >
         <Scripts>
             <%--Framework Scripts--%>
             <asp:ScriptReference Name="MsAjaxBundle" />
             <%--Site Scripts--%>
         </Scripts>
     </asp:ScriptManager>

 

WebForms scripts

We are registering all the scripts and we are bundling them together as “WebFormsBundle”

 ScriptManager.ScriptResourceMapping.AddDefinition("WebFormsBundle", new ScriptResourceDefinition
     {
         Path = "~/bundles/WebFormsJs", 
         CdnPath = "https://ajax.aspnetcdn.com/ajax/4.5/6/WebFormsBundle.js", 
         LoadSuccessExpression = "window.WebForm_PostBackOptions", 
         CdnSupportsSecureConnection = true
     });

 

Bundle Definition

 bundles.Add(new ScriptBundle("~/bundles/WebFormsJs").Include(
                   "~/Scripts/WebForms/WebForms.js",
                   "~/Scripts/WebForms/WebUIValidation.js",
                   "~/Scripts/WebForms/MenuStandards.js",
                   "~/Scripts/WebForms/Focus.js",
                   "~/Scripts/WebForms/GridView.js",
                   "~/Scripts/WebForms/DetailsView.js",
                   "~/Scripts/WebForms/TreeView.js",
                   "~/Scripts/WebForms/WebParts.js"));

 

ScriptManager registration

The reason we have the Assembly and the Path attribute here is because ScriptManager special cases these scripts when it tries to load them, so we had to make some special arrangements with ScriptManager code to make this work Smile This is a process which is called deduping. Basically when the ScriptManager tried to load up these scripts, it can load them up from System.Web or the path attribute and eventually in this case, the ScriptManager dedupes the script references and serves the scripts from the path attribute. This is part of the “special arrangement” that we did in ScriptManager. We were very cautious when doing this given this code path was on a very critical path for mainline ScriptManager scenarios which we did not want to regress.

 <asp:ScriptManager runat="server" >
         <Scripts>
             <%--Framework Scripts--%>
             <asp:ScriptReference Name="WebForms.js" Assembly="System.Web" Path="~/Scripts/WebForms/WebForms.js" />
             <asp:ScriptReference Name="WebUIValidation.js" Assembly="System.Web" Path="~/Scripts/WebForms/WebUIValidation.js" />
             <asp:ScriptReference Name="MenuStandards.js" Assembly="System.Web" Path="~/Scripts/WebForms/MenuStandards.js" />
             <asp:ScriptReference Name="GridView.js" Assembly="System.Web" Path="~/Scripts/WebForms/GridView.js" />
             <asp:ScriptReference Name="DetailsView.js" Assembly="System.Web" Path="~/Scripts/WebForms/DetailsView.js" />
             <asp:ScriptReference Name="TreeView.js" Assembly="System.Web" Path="~/Scripts/WebForms/TreeView.js" />
             <asp:ScriptReference Name="WebParts.js" Assembly="System.Web" Path="~/Scripts/WebForms/WebParts.js" />
             <asp:ScriptReference Name="Focus.js" Assembly="System.Web" Path="~/Scripts/WebForms/Focus.js" />
             <asp:ScriptReference Name="WebFormsBundle" />
             <%--Site Scripts--%>
  
         </Scripts>
     </asp:ScriptManager>

 

When you run this page you will get script references as follows

 <script src="/bundles/MsAjaxJs?v=J4joXQqg80Lks57qbGfUAfRLic3bXKGafmR6wE4CFtc1" type="text/javascript"></script>
 <script src="/bundles/WebFormsJs?v=q9E9g87bUDaS624mcBuZsBaM8xn2E5zd-f4FCdIk2cA1" type="text/javascript"></script>
     <script type="text/javascript">

 

So these were the improvements that happened in the ScriptManager control for registering scripts. In case you want to refresh your memory, ScriptManager was greatly enhanced in ASP.NET v4.0 and Dave Reed had a fantastic post about it, which I strongly recommend to go through.

Comments

  • Anonymous
    November 25, 2012
    It would be nice if it worked. Method PreApplicationStartCode.AddMsAjaxMapping is missing in latest System.Web.Optimization.dll installed as latest version through NuGet package.

  • Anonymous
    November 26, 2012
    These methods are defined in “AspNet.ScriptManager.jQuery” and “AspNet.ScriptManager.jQuery.UI.Combined" packages

  • Anonymous
    March 20, 2013
    It would be nice if you shade a light on functionality of ScriptManager within "Source" view of VS2012 designer. We use extended ScriptManager, which main purpose is to enable intellisense for js files embedded in our dll. It does a rather simple thing: within constructor it checks for desing mode and adds new ScriptReference for each embedded js file. However, if default asp:ScriptManager is replaced by ig:WebScriptManager, then intellisense for javascript stops to work. Our objects/controls do not have any members and actually almost all objects/functions including $find, $addHandler, etc. are lost as well. None of break points in debugger under "Source" view is hit (any our control or extended ScriptManager), though they hit in "Design" view. It looks like that "Source" view of VS2012 does not use dll codes at all, but reflected names or something like that. It would be great if you redirect me to some documentation which explains how to implement javascript intellisense for extended ScriptManager. Thanks.

  • Anonymous
    July 24, 2013
    PreApplicationStartCode.AddMsAjaxMapping is missing but you can define it by yourself: <pre>        private static void AddMsAjaxMapping(string name, string version, string loadSuccessExpression) {            ScriptManager.ScriptResourceMapping.AddDefinition(name, new ScriptResourceDefinition {                Path = "~/Scripts/WebForms/MsAjax/" + name,                CdnPath = "//ajax.aspnetcdn.com/ajax/" + version + "/" + name,                LoadSuccessExpression = loadSuccessExpression,                CdnSupportsSecureConnection = true            });        } </pre>

  • Anonymous
    November 19, 2013
    Ajax is broken this way, when you want LoadScriptsBeforeUI="false" in scriptmanager. (for example jquery and other your script at end of page)... Any solution to this? Without remapping framework script this work. Script manager render ajax files at top of page, other scripts at end of page.

  • Anonymous
    December 26, 2013
    Good morning. It looks like everyone is trying to make a point but NONE is straight forward relating to a simple (2 actually!) issues: what is the impact - apart from Viktor Snezhko - on applications and, the most important: who is answering who here? You guys can manage to be polite enough by starting a comment using: In response to...., can't you? Regarding these new development technologies I pointed my career to Project Management. But, I could learn ASP.Net in VS in a week(MVC, EF, C#). Understand "learn" here as "barely grasp". In addition, I search for Forums ad Blogs which present themselves as reliable and, most of the time, I find confusing discussion that not come clear after all. Noticed the timeline? Yes, it started Nov 26, 2012, then at the same day, by the blogger's owner, then after 4 months by Viktor, then after that 5 months and, finally, a year after the 1st post. This is quite confusing. Do you know how many times all of these you are talking about has been under "eagles eyes" from developers during these timeline? Guys, please don't fill the web with timeless and useless discussion. You'd be better off! Regards to all.

  • Anonymous
    December 02, 2014
    This is Incomplete Article. Please provide a sample project example, implementation of method "AddMsAjaxMapping" not found.

  • Anonymous
    July 21, 2015
    Have explained well for ScriptManager references bundling. But how can we achieve the bundling for ScriptManagerProxy scriptreferences bundling in aspx/ascx of page under masterpage.

  • Anonymous
    September 29, 2015
    The comment has been removed