Introduction
We've been writing a public API using WCF lately. After ironing out a few config issues and writing a few HTTP modules to make our API truly RESTful we were able to provide SOAP, REST / XML, REST / JSON and MS Ajax endpoints out of one service class. Pretty nice!
I've been pretty happy using MS AJAX and web service javascript proxies in the past, and when Atlas came out we started supporting its "preview" features (UI, Glitz, XML-Script etc...) so I wanted to leverage our existing javascript controls. But I'm also completely sold on the new ASP/NET MVC framework and probably won't ever go back to the postback model and its viewstate nightmare.
Here's the problem. As you probably know, by default MS Ajax requires a ClientScriptManager on your page, which itself requires a form runat="server" around it. While you can definitely do this using MVC, it kind of defeats the purpose.
The Code
After looking around and trying a few things, this is how we got it to work while still benefitting from the ScriptManager's script handling capabilities:
- Download the AJAX 3.5 javascript library.
- Place the javascript files somewhere in your MCV application (we'll use a "js" folder as an example).
- First we need to hack the master page to enable ScriptManager functionality without usinga server form. On your Master Page, do the following:
- add runat="server" to your tag
- in the the header ("head" tag), add a placeholder:
<asp:placeholder id="lit_Header" oninit="lit_Header_Init" runat="server" />
And place this in your Master Page's code-behind:
protected void lit_Header_Init(object sender, EventArgs e)
{
((PlaceHolder)sender).SetRenderMethodDelegate(delegate(HtmlTextWriter output, Control container)
{
typeof(ClientScriptManager).GetMethod("RenderClientScriptBlocks", BindingFlags.Instance BindingFlags.NonPublic).Invoke(this.Page.ClientScript, new object[] { output });
});
}
The above code will enable ScriptManager's script handling capabilities in all the pages that implement your master page.
- On the AJAX-enabled page or your master page's code behind, register your scripts using the ScriptManager's static methods (you can do this by overriding onload or onprerender)
protected override void OnLoad(EventArgs e) {
#if DEBUG
ScriptManager.RegisterClientScriptInclude(this, typeof(Page), "MicrosoftAjax", VirtualPathUtility.ToAbsolute("~/js/MicrosoftAjax.debug.js"));
#else
ScriptManager.RegisterClientScriptInclude(this, typeof(Page), "MicrosoftAjax", VirtualPathUtility.ToAbsolute("~/js/MicrosoftAjax.js"));
#endif
base.OnLoad(e);
}
ComponentArt users: keep reading
We use ComponentArt Web.UI (2008.1 for ASP.Net 3.5). After using the above code our AJAX code worked fine... as long as we didn't have any ComponentArt control in the page. When we included a tabstrip, all hell broke loose. Here's why.
CA components are ASP.NET AJAX aware. If the AJAX javascript library is present, they use it. If not, they create some objects to emulate basic Sys.* capabilities. They do this by checking whether a ScriuptManager instance is located on the page, which makes sense. But in our AJAX implementation, we rely on the ScriptManager's static methods to manually include the Ajax library. Therefore Web.UI can't find a ScriptManager and includes javascsript that conflicts with Microsoft's and breaks it. What we need to do is add one more hack to tell the Web.UI javascript controls the Ajax library is present. This is actually really easy. Just edit your AJAX-enbabled page or Master Page's code behind (we're adding one script block to the above code):
protected override void OnLoad(EventArgs e)
{
#if DEBUG
ScriptManager.RegisterClientScriptInclude(this, typeof(Page), "MicrosoftAjax", VirtualPathUtility.ToAbsolute("~/js/MicrosoftAjax.debug.js"));
#else
ScriptManager.RegisterClientScriptInclude(this, typeof(Page), "MicrosoftAjax", VirtualPathUtility.ToAbsolute("~/js/MicrosoftAjax.js"));
#endif
ScriptManager.RegisterClientScriptBlock(Page, typeof(Site), "ComponentArt_Atlas", "window.ComponentArt_Atlas=1;", true);
base.OnLoad(e);
}
The above code sets a global javascript variable ("ComponentArt_Atlas") to "1" at the top of the page. The is the variable all Web.UI controls to check before rendering themselves.
Service proxies and other .js files
Last step - including the MS Ajax web service client proxies. Just like we registered the framework, we can now use the ScriptManager to register other javascript includes. Just edit your page or user control's code behind to register those scripts:
protected void Page_Load()
{
#if DEBUG
ScriptManager.RegisterClientScriptInclude(this, typeof(Page), "MicrosoftAjaxPreview", VirtualPathUtility.ToAbsolute("~/js/PreviewScript.debug.js"));
ScriptManager.RegisterClientScriptInclude(this, typeof(Page), "MicrosoftAjaxPreviewGlitz", VirtualPathUtility.ToAbsolute("~/js/PreviewGlitz.debug.js"));
ScriptManager.RegisterClientScriptInclude(this, typeof(Page), "PresentationService", VirtualPathUtility.ToAbsolute("~/services/MyService.svc/jsdebug"));
#else
ScriptManager.RegisterClientScriptInclude(this, typeof(Page), "MicrosoftAjaxPreview", VirtualPathUtility.ToAbsolute("~/js/PreviewScript.js"));
ScriptManager.RegisterClientScriptInclude(this, typeof(Page), "MicrosoftAjaxPreviewGlitz", VirtualPathUtility.ToAbsolute("~/js/PreviewGlitz.js"));
ScriptManager.RegisterClientScriptInclude(this, typeof(Page), "MyService", VirtualPathUtility.ToAbsolute("~/services/MyService.svc/js"));
#endif
}


0 comments:
Post a Comment