First published on TECHNET on Jun 04, 2013
I worked on a case where the problem was that PageMethods won’t execute from within a SharePoint 2013 application page (page deployed to the _layouts folder). To replicate the problem, I had a custom master page deployed through a VS 2012 solution with the following definition within the <body/> element.
<script type="text/javascript">
function CallMe() {
alert("called from master page");
// call server side method
PageMethods.TestMethod(OnRequestComplete, OnRequestError);
}
function OnRequestComplete(res, userContext, methodName) {
alert(res);
}
function OnRequestError(error, userContext, methodName) {
if (error != null) {
alert(error.get_message());
}
}
</script>
And I had my TestMethod defined in my application page as shown below.
using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Web.Services;
namespace SPProj.Layouts.SPProj
{
public partial class ApplicationPage1 : LayoutsPageBase
{
protected void Page_Load(object sender, EventArgs e)
{
Button1.Attributes.Add("onclick", "javascript:CallMe()");
}
[WebMethod]
public static string TestMethod()
{
return "Hello";
}
}
}
Notice the ‘onclick’ attribute added to the server-side button control in the Page_Load event. The idea is, when this page is loaded and the button is clicked, we call the “CallMe()” function defined in the master page. And this client-side function will call the server-side TestMethod method by virtue of having PageMethods enabled. But this is what happens in SharePoint 2013.
The page loads.
Click the button.
Click OK on this alert and the second message box won’t come up. You might have already noticed the JavaScript error in IE status bar. Double-click on it and you’ll see this error.
Apparently, this is because of anonymous JavaScript functions being pushed to the page by the new web-scoped feature called “Following Content”. Here’s a snippet from IE Developer Tools. For brevity, only relevant snippet is shown.
PageMethods.set_path("applicationpage1.aspx");
PageMethods.TestMethod= function(onSuccess,onFailed,userContext) {
/// <param name="succeededCallback" type="Function" optional="true" mayBeNull="true"></param>
/// <param name="failedCallback" type="Function" optional="true" mayBeNull="true"></param>
/// <param name="userContext" optional="true" mayBeNull="true"></param>
PageMethods._staticInstance.TestMethod(onSuccess,onFailed,userContext); }
// from custom action with id = "FollowingCalloutScriptBlock"
(function(){
if (typeof(_spBodyOnLoadFunctions) === 'undefined' || _spBodyOnLoadFunctions === null) {
return;
}
_spBodyOnLoadFunctions.push(function() {
The problem is that when PageMethods are pushed to the page, a semi-colon isn’t added to properly terminate the JavaScript block. This is actually not a SharePoint but ASP.NET issue that PageMethods are not properly block terminated. Since the following JavaScript code is anonymous function at runtime it’s considered to be in the same block and executed, which causes the object expected error.
There are multiple ways to fix it.
1. Deactivate “Following Content” feature. Not a good one, but if this feature is not being used (which is highly unlikely), then simply deactivating it resolves this error.
2. Modify the “Following Content” feature. Change the 2 functions defined in the ScriptBlock of <CustomAction/> element to be named functions instead of anonymous. Deactivate/Uninstall “Following Content” feature. Install/Activate “Following Content” feature. THIS OF COURSE, IS UNSUPPORTED .
3. Override the Render method in the application page and inject a semi-colon character. Below is the modified application page code.
using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Web.Services;
namespace SPProj.Layouts.SPProj
{
public partial class ApplicationPage1 : LayoutsPageBase
{
protected void Page_Load(object sender, EventArgs e)
{
Button1.Attributes.Add("onclick", "javascript:CallMe()");
}
[WebMethod]
public static string TestMethod()
{
return "Hello";
}
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
System.Web.UI.ScriptManager.RegisterClientScriptBlock(this, typeof(ApplicationPage1), "semicolon", ";", true);
base.Render(writer);
}
}
}
Once you deploy this solution, browse to the application page and watch the scripts pushed using IE Developer Tools, you’ll see this.
PageMethods.set_path("applicationpage1.aspx");
PageMethods.TestMethod= function(onSuccess,onFailed,userContext) {
/// <param name="succeededCallback" type="Function" optional="true" mayBeNull="true"></param>
/// <param name="failedCallback" type="Function" optional="true" mayBeNull="true"></param>
/// <param name="userContext" optional="true" mayBeNull="true"></param>
PageMethods._staticInstance.TestMethod(onSuccess,onFailed,userContext); }
;
// from custom action with id = "FollowingCalloutScriptBlock"
(function(){
if (typeof(_spBodyOnLoadFunctions) === 'undefined' || _spBodyOnLoadFunctions === null) {
return;
}
_spBodyOnLoadFunctions.push(function() {
Notice that “;” character after the PageMethods declaration just before the anonymous functions from “Following Content” feature starts. Now, when the application page is browsed.
And the button is clicked.
The first alert is displayed and when you click OK, you’ll see the second alert as well.
That resolves this issue. Hope this post was helpful.
Cross post from http://blogs.msdn.com/sridhara
Welcome to the SharePoint Blog! Learn best practices, news, and trends directly from the SharePoint team.