ScriptManager (AjaxControlFramework.ScriptManager)

Overview

The ScriptManager is one of the out-of-the-box UI controls that comes packaged with the AJAX Control Framework. It lets you reference JavaScript files and scripts in embedded resources on any page, any User Control or even programmatically, and the scripts will all be rendered uniformly at the location you specify them. You can tell the scripts you reference in your ScriptManagerss to load either at the bottom of the <head>, the bottom of the <body> or "here" (right where you declared them). You can have as many ScriptManagers in your markup as you want and reference the same scripts each time, and the ScriptManager ensures that there are no duplicates rendered. You can also specify dependencies so that your scripts will always load in the correct order.

Read on before you think "this isn't really necessary." The ScriptManager complements the coding style and structure of the the AJAX Control Framework VERY well.

Remarks

I've you've ever called the System.Web.UI.ClientScriptManager.RegisterClientScriptResource method then you've probably noticed that ASP.NET puts the <script> reference near the top of the <body> element. The best practice for <script> elements is to declare them at the bottom of the <body> so that their loading doesn't hold up the intialization of the rest of the DOM (this can be noticable if there are ALOT of embedded JavaScript files). Except for maybe a few straggling images, everything a page needs would be loaded by the time a script executes, ensuring any code that requires a DOM element to be present will be able to find it and execute properly. Often times when there are dependencies between script files having certain ones render at the top of the body, beyond your control, causes heartache for the developer. I've experienced this particular heartache many times.

Now lets say you're developing a module for a Content Management System like DotNetNuke. It's good practice, for the sake of componentization, to declare your <script> elements in whatever Page or User Control it is needed within the scope of your module or plugin. In the case of DotNetNuke, you would declare the scripts your module needs in one of your module's .ascx files rather than in the Default.aspx page of the site. This ensures that the script file doesn't get loaded unless that particular module is on the current page being viewed (if your module uses 4 or 5 different JavaScript files it would be nonsense to have all of them load on every page). Once again, this is good practice if you're trying to achieve componentization. But, you may break the best practice of putting all your scripts at the bottom of the page if your components are User Controls. When you include a <script> element in a User Control, the script renders wherever the User Control renders, whether that be in the middle of the page's markup or at the bottom. EVEN WORSE, if the Default.aspx file references the jQuery library just like your module does, then jQuery will be loaded more than once. AHHHHH!!!!

So it looks like you're gonna have to make a compromise somewhere if you want to avoid load-dependencies, duplicate script declarations and stay strong in the ways of componentization. No thanks. I refuse. I believe it is your duty to refuse to accept such a compromise as well. The AJAX Control Framework comes to our rescue in this case with the ScriptManager control. Here are the 7 major features of the ScriptManager, all of which resolve the issues mentioned above:
  • Declaratively specify where scripts are to be rendered, at the bottom of the <body>, at the bottom of the <head> or exactly where they were declared.
  • Maintains a global collection of script references behind-the-scenes, ensuring no duplicate scripts are rendered.
  • Sorts script references before they are rendered by dependencies, which are declared via markup or procedural code.
  • Allows for programattic additions of references to the manager so that non-visual components (especially custom, controls deriving from WebControl and Control).
  • Can be declared any number of times within a single page instance and not incur the "Only one instance of a ScriptManager can be added to the page" exception.
  • Doesn't automatically load the ASP.NET AJAX library, just in case you didn't want to use it and still be able to manager and centralize all your script references.
  • Doesn't leave a footprint in ViewState (just in case you were worried).
  • Allows for inline script blocks.

Important Note - If you choose to render your scripts at the bottom of the <body>, you have to include runat="server" in the start tag of the body element. The same concept applies if you want to render your scripts in the <head>, be sure to include runat="server" in the start tag of the head element.

Examples

Declarative Example

The "RenderLocation" property specifies where to the render the references declared in the <Scripts> section of specific instance of the ScriptManager. It has 3 possible values:

Head - causes the scripts to be rendered at the bottom of the <head>.
Body - causes the scripts to be rendered at the bottom of the <body>.
Here - causes the scripts to be rendered right where they are in the markup...wherever that may be.

The <Scripts> section will be the collection within which all of your <ajaxControl:Script> references must be declared.

The <ajaxControl:Script> is a nested control that represents an individual JavaScript block that will be rendered on the page. It has the following properties:
  • Type - The type of script the current reference is. The possible values are: "Embedded", "File" and "Inline". "Embedded" means the script is referencing a JavaScript file that is embedded as a resource of an assembly.
  • Name - required - A unique name for the script reference that will identify it globally among all other scripts to be rendered on the page.
  • Qualified Name - required if Type = "Embedded" - The fully qualified embedded resource name of the JavaScript file.
  • Assembly - The name of the assembly that contains the embedded script resource specified by the "QualifiedName" property. If you're not sure what that is, by convention the assembly name is everything before the ".dll" in an assembly file name.
  • Dependencies - This is a comma delimited list of other script references (via their "Name" property) that must be rendered before the one in context is. Circular references will cause a runtime exception to be thrown, so don't list any scripts that include the current script in their "Dependencies" property.
  • SourceCode - required if Type = "Inline" - you can either cram all that text in the property or just set the content of the <ajaxControl:Script> with the inline JavaScript text - JavaScript code that will be rendered inline within a <script> element.
  • Src - required if Type = "File" - The virtual file path of the JavaScript file to be rendered. This file path may be prepended with "~/".

When using server variables with the ScriptManager, you're safest when using data binding syntax (<%# %>). Doesn't this suck?! You're stuck with using late-bound processing if you want to include inline scripts with server variables when using the ScriptManager. Well, not for long. Hang in there and wait for the JavaScriptBuildProvider I'm working on to be complete. You'll be able to reference server variables in seperate .jsx files instead of inline.

Important Note - If you choose to render your scripts at the bottom of the <body>, you have to include runat="server" in the start tag of the body element. The same concept applies if you want to render your scripts in the <head>, be sure to include runat="server" in the start tag of the head element.

Markup

<ajaxControl:ScriptManager runat="server">
    <Scripts>
        <ajaxControl:Script runat="server" Name="jQuery" Src="~/js/jquery-1.4.1.min.js" />
    </Scripts>
</ajaxControl:ScriptManager>

Inline Script Example

Here is an example of an inline script reference that is rendered in the <head>. Don't forget to set the Type property of the <ajaxControl:Script> to "Inline".

Markup

<ajaxControl:ScriptManager runat="server" RenderLocation="Head">
    <Scripts>
        <ajaxControl:Script runat="server" Name="InlineAlert" Type="Inline">
            alert("Oh man...isn't this annoying.");
        </ajaxControl:Script>
    </Scripts>
</ajaxControl:ScriptManager>

Embedded Resource Example

Here is an example of an embedded resource script reference. Take notice as to how both the "Assembly" and "QualifiedName" properties are set. They are both required in order for the ScriptManager to resolve the embedded resource correctly. Also, don't forget to set the Type property of the <ajaxControl:Script> to "Embedded".

One last thing to take notice of. I've listed two Script references here: 1 that is of an embedded resource and 1 that is a reference to a script file. Both have the same name so there will be a duplicate within the internal script reference collection. This results in only the first of the two to actually be rendered.

Markup

<ajaxControl:ScriptManager runat="server" RenderLocation="Here">
    <Scripts>
        <ajaxControl:Script runat="server" Name="Yoohoo" Type="Embedded" QualifiedName="AjaxControlFramework.Docs.js.Yoohoo-embedded.js" Assembly="AjaxControlFramework.Docs" />
        <ajaxControl:Script runat="server" Name="Yoohoo" Src="~/js/Yoohoo.js" />
    </Scripts>
</ajaxControl:ScriptManager>

Programmatic Example

Here is an example of how you can programmatically control add script references and set their dependencies. Shown below is a snippet from the source code of the AjaxControlFramework.ScriptStrategy class. Notice that we're first creating an instance of the EmbeddedScriptReference class that will represent the "AjaxControlFramework.AjaxControl.release.js" JavaScript file that is embedded as a resource within the AjaxControlFramework assembly.

Code-Behind

EmbeddedScriptReference frameworkScriptReference = new EmbeddedScriptReference("AjaxControl", "AjaxControlFramework.AjaxControl.release.js", "AjaxControl");
frameworkScriptReference.Dependencies.Add("System.Web.WebForms");

InternalScriptManager.Instance.RegisterScript(frameworkScriptReference, ScriptRenderLocation.Body);

Last edited May 25, 2010 at 10:48 AM by peanutbutterlou, version 5

Comments

No comments yet.