SharePoint 2010中的客户端AJAX应用——ASP.NET AJAX模板

WCF Data Services是SharePoint 2010中一个极具吸引力的新特性。然而,因为它的强大,直接对其进行编程仍然会有点痛苦。幸运的是,一个新的相关技术 —— ASP.Net AJAX模板 – 可以完美的与WCF Data Service进行集成,并允许我们快速构建优雅的,可维护的和反应迅速的AJAX应用程序。 

在本文中,我将详细描述ASP.Net AJAX模板,并在Visual Studio 2010中一步一步的构建一个非常简单的应用程序页面。接着使用ASP.Net AJAX模板来显示SharePoint 2010中通过WCF Data Services发布的JSON格式的列表数据。 

什么不是模板? 

单纯从技术层面解释什么是ASP.Net AJAX模板可能并不容易。不妨先假设没有这项技术,看看传统方式是如何解决问题的。如果SharePoint传递给你一个JavaScript中的JSON对象的数组,现在的你会如何将其转换成HTML? 

最常见的答案似乎就是JQuery的.append()语句了。代码大致如下:

for (var i = 0; i < userStories.length; i++) {
$(
"#userStories").append("<div class='userStoryCard'>" +
userStories[i].Title 
+ "</div>");
}

这种类型的解决方案的问题,或者说任何没有使用模板引擎的解决方案,事实上都无法清楚的分离数据访问逻辑和界面展现。事实上,这种情形很像回到了经典的Active Server Pages风格,可维护性很差,代码一塌糊涂。随着我们不断的从SharePoint获取数据,整个代码会变得更加杂乱。 

模板的目标是使你不必编写上面那样的代码,使用模板后的代码应该像下面这样:: 

<div class="userStoryCard">{{ Title }}</div>

整洁,漂亮。理想状态下模板应提供这样一个的解决方案: 

  • 最大限度地减少管道(plumbing)代码 
  • 清晰的分离数据访问逻辑和界面展示 
  • 简化存回服务器端的数据 
  • 并且没有ViewState!  

ASP.Net AJAX模板的昨天,今天和明天 

如果你读过Scott Guthrie的博文jQuery templating engine  ,或者你曾非常不容易的看到过准确介绍jQuery模板的官方文档,你就会惊讶于为什么ASP.Net AJAX模板与其如此之符合,这也正是微软构建客户端AJAX应用的愿景。 
首先,在ASP.Net AJAX项目还是beta阶段时,模板就已经存在了。当ASP.Net AJAX 4.0从测试转到正式发布后,模板JavaScript文件被纳入到AJAX ControlTooklit CodePlex项目(语法稍作修改)。这就是为什么你会发现今天网上找到的大多数例子都稍微有些过时。
至于jQuery的 模板引擎,据Stephen Walther(ASP.Net MVC团队的项目经理)所说,他们“未来不会投入过多精力到[ASP.Net AJAX的模板]”,因为他们将会把重点放在jQuery模板引擎。那么我们何必学习一门不会有所发展的技术呢?原因之一是jQuery模板引擎仍非常年轻(撰写本文时还处于原型阶段),而ASP.Net AJAX模板已正式发布,可以直接用于生产环境。另一方面,其语法很接近,想必将来升级也不会成为一个大工程(理论上如此)。 

Visual Studio 2010和SharePoint 2010 

在进入模板学习前,让我们先来快速看一下如何在Visual Studio 2010中构建一个应用程序页面。这是我们将来编写代码的基础。当然你可以把所有代码都放在一个内容编辑器Web部件里,但是Visual Studio提供了更好的智能提示和调试的支持,因此我们选择在应用程序页中运行。

为了在Visual Studio 2010中建立一个简单的应用程序页面,先选择“新建项目”,然后导航到SharePoint 2010模板分类,选择“空白SharePoint项目”

点击确定后,Visual Studio会弹出SharePoint自定义向导:

该窗口要求你选择一个站点用于调试。当你按F5键(开始调试)时,Visual Studio 2010会进行wsp文件打包,然后将其部署到这里你填写的URL对应的站点上,同时附加上对应的w3wp进程,并打开一个浏览器显示该sharepoint站点。这样,你就立即拥有一个调试环境了。

如果你对这一过程印象不深刻,那么你可能没有做过多少WSS 3.0的开发——但请相信我,这个改进大大节省了我们的时间。尽管在本例中我们不会写任何服务器端代码,但仍旧可以体会到微软在SharePoint 2010上倾注了不少的心力。 

当Visual Studio完成项目初始化后,选择项目->添加新项,然后选择应用程序页。 

Visual Studio接下来会自动生成一个完全套用SharePoint母版页并包含内容控件的页面。

代码
<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="UserStories.aspx.cs" Inherits="PreDemo.Layouts.PreDemo.UserStories" DynamicMasterPageFile="~masterurl/default.master" %>
<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
</asp:Content>
<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
Hello World!
</asp:Content>
<asp:Content ID="PageTitle" ContentPlaceHolderID="PlaceHolderPageTitle" runat="server">
应用程序页
</asp:Content>
<asp:Content ID="PageTitleInTitleArea" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server" >
我的应用程序页
</asp:Content>

(“Hello World!”是我写的)。现在如果你按F5键或者点击生成->部署解决方案,则Visual Studio会打包该应用程序页到一个wsp文件并将其部署到SharePoint。如果你导航到该应用程序页的话(例如本例中为http://contoso14/_layouts/PreDemo/UserStories.aspx)则会看到如下的显示:

很震撼吧!你真的应该如此。我们没有编写冗长的,重复的代码,或者容易出错的CAML,没有运行任何DOS(或PowerShell)脚本,没有手工部署到GAC,甚至没有注意到后台自动生成的Feature.xml文件,然而,我们已经部署完了。

安装ASP.Net AJAX模板

ASP.Net AJAX模板的安装比较棘手。为了得到它你需要先在CodePlex上下载最新推出的AJAX控件工具包源代码(本例中下载下来的是AjaxControlToolkit-9c860ac12ae9.zip)。并在Visual Studio(它的项目中包含了Visual Studio 2008和2010两个版本)中编译(编译时还需要安装Microsoft Ajax Minifier 2.0)然后将Javascript文件复制到SharePoint的layouts目录下。需要复制的目录有:

从 AjaxControlToolkit.Source\SampleWebSites\AjaxClientWebSite\Scripts; 复制到

    C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\ \14\TEMPLATE\LAYOUTS\Scripts

还有从 AjaxControlToolkit.Source\Client\MicrosoftAjax\Templates; 复制到

    C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\ \14\TEMPLATE\LAYOUTS\Scripts

或者采用更符合标准的做法,将其复制到Visual Studio项目的layouts目录中。

代码

Javascript文件都就位后,我们就可以编写最简单的模板页代码了:

<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="UserStories.aspx.cs" Inherits="PreDemo.Layouts.PreDemo.UserStories" DynamicMasterPageFile="~masterurl/default.master" %>

<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
<style type="text/css">
.sys-template 
{
    display
:none;
}
</style>
<script src="/_layouts/Scripts/MicrosoftAjax/Start.js" type="text/javascript"></script>
<script src= "/_layouts/Scripts/MicrosoftAjax/MicrosoftAjax.js" type="text/javascript"></script>
<script type="text/javascript">
Sys.require([
Sys.components.dataView,
Sys.components.openDataServiceProxy,
Sys.scripts.jQuery
]);
Sys.onReady(
function () {
    
var dataSource = Sys.create.openDataServiceProxy('/_vti_bin/ListData.svc');


    Sys.query(
"#userStoriesList").dataView({
        dataProvider: dataSource,
        fetchOperation: 
"UserStories",
        feachParameters: { orderby: 
'标题' },
        autoFetch: 
"true"
    });

});
</script>
</asp:Content>
<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
Hello World!
<ul id="userStoriesList" class="sys-template">
<li>{{ 标题 }}</li>
</ul>
</asp:Content>
<asp:Content ID="PageTitle" ContentPlaceHolderID="PlaceHolderPageTitle" runat="server">
应用程序页
</asp:Content>
<asp:Content ID="PageTitleInTitleArea" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server" >
我的应用程序页
</asp:Content>

其中的UserStories是我的一个任务列表。我事先填充了一些示例数据。

跳过JavaScript部分,我们先来看Main content一节中名为userStoriesList的<ul>无序列表。 其中的<li>元素包围着一个有趣的符号:{{ 标题 }}。这样就声明了一个绑定到SharePoint中ListItem的标题字段。我们也可以改成任何其他的列,如“优先级”,或者在其中添加JavaScript,如{{ String.format("{0:yyyy-M-dd}", 修改时间) }}。

注意PageHead的脚本引用。引用MicrosoftAjax.js使得我们可以使用ASP.Net AJAX的核心功能。Start.js使得我们可以使用Sys.require功能,以便导入其他具有依赖关系的Javascript。

至此,我们还没有导入足够的JavaScript以提供我们需要的模板功能。这部分工作由Sys.require来完成。声明了1.DataView对象,用于完成主要的模板工作;2.openDataServiceProxy,知道如何与SharePoint的ADO.Net Data Service进行通讯;3. jQuery。如果你用Firebug看的话就会发现Sys.require加载了哪些额外的.js文件,就像MicrosoftAjaxTemplates.js。而且是按照正确的顺序加载到页面上的。这种方式保证了页面不会放上很多不需要的Javascript。 

真正的奇迹发生在Sys.onReady里,其功能是在DOM加载完成后执行一次。首先它实例化了一个openDataServiceProxy对象,负责与一个oData端点进行通讯,在我们的例子中是一个ADO.Net Data Services。

它做的第二件事情是实例化一个DataView对象,并将其关联到userStoriesList元素。fetchOperation参数告诉它从哪个列表获取ListItems(本例中为UserStories列表)。fetchParameters参数告诉它如何对数据进行排序,筛选或分页。

DataView实例化后(由于autoFetch被设为true) 会通过其dataProvider检索JSON数据,并为每个返回的行重复所关联的DOM元素的innerHTML,而且以实际的数据代替绑定语法。看起来似乎很简单,但在将来的博文中你会看到它很快会变得很复杂。

最后一个要注意的是sys-template类。这是一个在ASP.Net AJAX中预定义的CSS类,当完成渲染工作后,由ASP.Net AJAX将其设置为display:block。因此,我们需要为sys-template创建一个CSS类并设为display:none,这样在页面加载过程中最终用户就看不到你的模板代码了。

现在,如果你点击生成->部署解决方案,就会看到如下的显示: 

总结

当然,可能如果用服务器端代码编写这个功能的话实现起来更容易。这个话题先搁在这儿,我们的解决方案还有很多潜力可挖。例如,从哪个层面上与jQuery结合,可以使界面更美观?如何将数据写回到上下文服务器端?如何实现主-子关系?我们将在随后的博文中一起来探讨这些问题。

 

参考资料

Client Side AJAX Applications in SharePoint 2010-Part3:ASP.Net AJAX Templating 101 

ASP.NET AJAX: Client Postbacks cause a Sys.ArgumentTypeException 

 

posted @ 2010-07-22 04:27  Sunmoonfire  阅读(2989)  评论(1编辑  收藏  举报