SharePoint 2010中的客户端AJAX应用——对话框显示详细信息页

 

本文是SharePoint 2010客户端AJAX应用系列的一部分。

ASP.Net AJAX模板是一门全新的引人注目的客户端技术,允许开发者快速构建AJAX易于维护的交互式应用程序。由于ASP.Net AJAX模板和SharePoint 2010都支持oData协议,因此两者结合在一起将是一个强大的组合。

SharePoint 2010 之所以可以带给人们Web 2.0的外观和感觉很大一部分要归功于其弹出式模式对话框的使用。为了进一步丰富上一篇中的AJAX应用,我们在每张卡片上挂接一个操作,打开一个对话框以便对该卡片做更细致的处理。在之前的使用SharePoint 2010模式对话框一文中,我们学习了如何在模式对话框中打开远端的页面,以及如何响应对话框确定或取消事件。本文中的模式对话框会更进一步,基于本地的HTML内容打开对话框。

首先,我们在前文中做好的索引卡上添加一个编辑图标。我们将在其上挂接打开模式对话框的操作:

?
1
2
3
4
5
6
7
8
< div class = "userStoryTitle" >
     {{ 标题 }}
     < span class = "userStoryButtons" >
         < a href = "#" onclick = "javascript:openDialog(); return false;" >
             < img src = "/_layouts/images/edititem.gif" />
         </ a >
     </ span >
</ div >

为了先简单测试一下打开对话框的效果,同时也复习一下前面学习的模式对话框的使用,我们编写如下的打开对话框代码:

?
1
2
3
4
5
6
7
8
9
function openDialog() {
     var options = {
         width: 800,
         height: 600,
         title: "User Story" ,
     };
     SP.UI.ModalDialog.showModalDialog(options);
}

显然硬编码的URL中的参数id不是最佳做法,这里只是作为示范。结果看起来像这样:

这是一个非常有用的技术,允许我们在不离开现有的SharePoint网页的情况下打开一个对话框,使用户可以直接浏览另一个网页。然而,在这里我们希望我们的应用程序中编辑的信息是在浏览器的内存里(通过ASP.Net AJAX模板存储数据)。该showModalDialog()函数可以支持这一方案,但要稍微复杂一些。

模式对话框方式打开本地HTML内容

首先,我们需要一个HTML元素用于弹出。作为用来测试的一个初稿,我们使用如下的html内容:

?
1
2
3
< div id = "userStoryDetails" >
     Hello World!
</ div >

由于我们传递给showModalDialog()的options参数支持一个'html'参数来替代'url'参数,因此猜测可能看起来我们只需要简单地在openDialog中获取到userStoryDetails元素并作为选项传递即可。然而,这一做法有一个问题。默认SharePoint的showModalDialog()函数将销毁传递给它的DOM元素。结果是对话框只可以打开一次,再次打开就会失败。

为了避免这种行为,我们可以在一个全局变量中缓存该DOM元素,而不是放在函数层中作为一个局部变量。代码如下所示:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var userStoryDetails;
  
Sys.onReady(function () {
     userStoryDetails = document.getElementById("userStoryDetails");
     ...
});
  
function openDialog() {
     var options = {
         html: userStoryDetails,
         width: 600,
         height: 300,
         title: "User Story",
     };
     SP.UI.ModalDialog.showModalDialog(options);
}

有了这个代码,我们就可以多次关闭和打开模式对话框了。结果如下所示:


接下来我们来处理对话框操作完成后的结果。在前面的文章中我们也介绍过做法。首先在对话框的代码中调用commonModalDialogClose(),然后在主窗口中实现回调函数,以便SharePoint在关闭弹窗时调用。
修改我们的userStoryDetails内容,增加调用commonModalDialogClose()的代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
< div id = "userStoryDetails" >
   < input
     type = "button"
     value = "确定"
     onclick = "SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.OK, '点了确定'); return false;"
     class = "ms-ButtonHeightWidth"
     />
   < input
     type = "button"
     value = "取消"
     onclick = "SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.cancel, '点了取消'); return false;"
     class = "ms-ButtonHeightWidth"
     />
</ div >

请注意commonModalDialogClose()第一个参数,是一个DialogResult。这个参数非常重要,因为我们的回调函数将通过此方法,区分不同的对话框关闭方式。例如,当用户点击在右上角的X时,SharePoint会传递DialogResult.cancel作为第一个参数。

commonModalDialogClose()的第二个参数会直接作为回调函数的第二个参数进行传递。

为了关闭对话框时对结果进行处理,我们可以为showModalDialog函数的options参数中增加一个dialogReturnValueCallback参数。结果如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function openDialog() {
     var options = {
         html: userStoryDetails,
         width: 800,
         height: 600,
         title: "User Story" ,
         dialogReturnValueCallback: onDialogClose
     };
     SP.UI.ModalDialog.showModalDialog(options);
}
  
function onDialogClose(dialogResult, returnValue) {
     if (dialogResult == SP.UI.DialogResult.OK) {
         alert( '( ^_^ )/~~拜拜!' );
     }
     if (dialogResult == SP.UI.DialogResult.cancel) {
         alert(returnValue);
     }
}

现在我们可以打开和关闭SharePoint模式对话框并处理结果。如果需要,我们还可以对模式对话框的options中的其他参数进行调整。例如的X,Y,allowMaximize,showMaximized和showClose。这方面的文档很少,希望MSDN上的这个页面在不久的将来能够更新。

接下来,我们要正式进行对话框内容的编写。

显示selectedData

为了可以显示当前选定的项目,我们将需要将弹出对话框中的HTML绑定到当前选中的列表项。下面是一个最简单的例子:

?
1
< div id = "userStoryDetails" class = "sys-template" >{{ 标题 }}</ div >

注意其中sys-template类的使用。在上一篇博文中我们说过,任何dataView依附的元素都需要加上这个类。用于在页面加载时将该元素设为display:none,dataView显示时会将其设回到display:block。

接下来,我们需要第二个dataView对象来绑定对话框中显示的HTML内容。

?
1
detailsDataView = Sys.query( "#userStoryDetails" ).dataView().get(0);

注意,我们并没有像第一个dataView那样设置dataProvider或fetchOperation。原因是我们将绑定其data属性到主dataView的selectedData属性,如下所示:

?
1
2
3
4
5
6
$create(Sys.Binding, {
            source: dataView,
            path: "selectedData" ,
            target: detailsDataView,
            targetProperty: "data"
        });

selectedData 是DataView的一个特殊属性,代表当前选定的项目。现在离我们完成主-子关系的应用场景已经非常接近了。剩下的主要任务就是告诉主DataView什么时侯更新 selectedData。我们可以通过在主DataView的模板中的某个元素上添加sys:command="select"来完成该任务。添加到打开该模式对话框的按钮上应该最合乎逻辑:

?
1
2
3
4
5
<span class= "userStoryButtons" >
           <a  href= "#" onclick= "javascript:openDialog(); return false;"  sys:command= "select" >
                 <img alt= "edit" src= "/_layouts/images/edititem.gif" >
           </a>
</span>

现在,我们完成了如下所示的逻辑:

显示主项对应的详细信息

到目前为止,我们所显示的信息过于单薄了。在后面的博文中,我们会实现对字段内容的编辑和保存功能。本文中,我们仅仅再多加些只读字段。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
< div id = "userStoryDetails" class = "sys-template" >
  < table class = "ms-formtable" width = "100%" >
     < tr >
       < td class = "ms-formlabel" width = "190" >标题:</ td >
       < td class = "ms-formbody" >{{ 标题 }}</ td >
     </ tr >
     < tr >
       < td class = "ms-formlabel" width = "190" >点数:</ td >
       < td class = "ms-formbody" >{{ 点数 }}</ td >
     </ tr >
     < tr >
       < td colspan = "2" nowrap>
         < span >创建于 </ span >
         < span >{{ String.format("{0:yyyy-M-dd h:m tt}", 创建时间) }}</ span >
  
         < input type = "button" name = "OK" value = "确定" class = "ms-ButtonHeightWidth"
           onclick = "SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.OK); return false;" />
         < input type = "button" name = "Cancel" value = "取消" class = "ms-ButtonHeightWidth"
           onclick = "SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.cancel,'点了取消'); return false;" />
       </ td >
     </ tr >
   </ table >
</ div >

关于上面的详细信息视图,有两处需要注意。其一,我们可以显示像点数这样的没有在主dataView出现过的字段(假设在列表中有这个字段)。之所以可以,是因为主dataView实际上默认情况下是下载了列表的所有字段。关于如何从相

关的列表中获取数据也是我们在将来博文中要讨论的一个话题。
其二,我们处理日期时间的方式是如此简单。String.format是AJAX库提供的一个方法,可以用于格式化SharePoint提供的酷似C#/VB中的日期格式,使其变成读者可以接受的格式。以下是我们做好的新的详细信息视图:

下面是至今为止整个应用程序的源代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
<%@ 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;
   }
   .userStoryBackground
   {
     background-image: url('Images/corkboard.png');
     width:695px;
     height:397px;
   }
   .userStoryCard
   {
     border: 1px solid #777777;
     width: 206px;
     height:124px;
     cursor: move;
     background-image: url('Images/blankcard.png');
     margin:4px;
   }
   .userStoryDescription
   {
     padding: 5px;
     line-height:1.2;
   }
   .userStoryTitle
   {
     font-weight: bold;
     padding: 2px 5px 0px 5px;
   }
.userStoryButtons
{
     position: absolute;
     right: 0px;
     padding: 2px 2px 0 0;
}
.userStoryButtons img
{
     border: 0 none;
}
</ style >
< script src = "/_layouts/Scripts/jQuery/jquery-1.4.1.js" type = "text/javascript" ></ script >
< script src = "/_layouts/Scripts/plugins/jquery-ui-1.8.2.custom.min.js" type = "text/javascript" ></ script >
< 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.openDataContext,
]);
     var dataContext;
     var dataView;
     var userStoryDetails;
     var detailsDataView;
  
  
     Sys.onReady(function () {
  userStoryDetails = document.getElementById("userStoryDetails");
  
         dataContext = Sys.create.openDataContext({
             serviceUri: "/_vti_bin/ListData.svc",
             mergeOption: Sys.Data.MergeOption.appendOnly
         });
  
         dataView = Sys.query("#userStoriesList").dataView({
             dataProvider: dataContext,
             fetchOperation: "UserStories",
             feachParameters: { orderby: '标题' },
             autoFetch: "true",
             rendered: onRendered
         }).get(0);
  
         detailsDataView = Sys.query("#userStoryDetails").dataView().get(0);
          
         $create(Sys.Binding, {
             source: dataView,
             path: "selectedData",
             target: detailsDataView,
             targetProperty: "data"
         });
     });
     function onRendered() {
         $(".userStoryCard").draggable({
             stop: onDragStop
         });
     }
     function onDragStop(event, ui) {
         var userStoryCard = ui.helper[0];
         var selectedUserStoryJsonObject = dataView.findContext(userStoryCard).dataItem;
         var newX = ui.position.left;
         var newY = ui.position.top;
         Sys.Observer.setValue(selectedUserStoryJsonObject, "X", newX);
         Sys.Observer.setValue(selectedUserStoryJsonObject, "Y", newY);
         dataContext.saveChanges();
     }
  
     function openDialog() {
         var options = {
             html: userStoryDetails,
             width: 580,
             height: 320,
             title: "User Story",
             dialogReturnValueCallback: onDialogClose
         };
         SP.UI.ModalDialog.showModalDialog(options);
     }
  
     function onDialogClose(dialogResult, returnValue) {
         if (dialogResult == SP.UI.DialogResult.OK) {
              
         }
         if (dialogResult == SP.UI.DialogResult.cancel) {
             
         }
     }
</ script >
</ asp:Content >
  
< asp:Content ID = "Main" ContentPlaceHolderID = "PlaceHolderMain" runat = "server" >
< div id = "userStoryDetails" class = "sys-template" >
  < table class = "ms-formtable" width = "100%" >
     < tr >
       < td class = "ms-formlabel" width = "190" >标题:</ td >
       < td class = "ms-formbody" >{{ 标题 }}</ td >
     </ tr >
     < tr >
       < td class = "ms-formlabel" width = "190" >点数:</ td >
       < td class = "ms-formbody" >{{ 点数 }}</ td >
     </ tr >
     < tr >
       < td colspan = "2" nowrap>
         < span >创建于 </ span >
         < span >{{ String.format("{0:yyyy-M-dd h:m tt}", 创建时间) }}</ span >
  
         < input type = "button" name = "OK" value = "确定" class = "ms-ButtonHeightWidth"
           onclick = "SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.OK); return false;" />
         < input type = "button" name = "Cancel" value = "取消" class = "ms-ButtonHeightWidth"
           onclick = "SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.cancel,'点了取消'); return false;" />
       </ td >
     </ tr >
   </ table >
</ div >
  
< div id = "userStoriesList" class = "sys-template userStoryBackground" xmlns:sys = "javascript:Sys" >
      < div  class = "userStoryCard" sys:style = "{{ 'left:'+X+'px;top:'+Y+'px;'}}" >
           < div class = "userStoryTitle" >
           {{ 标题 }}
           < span class = "userStoryButtons" >
               < a  href = "#" onclick = "javascript:openDialog(); return false;"  sys:command = "select" >
                 < img alt = "edit" src = "/_layouts/images/edititem.gif" >
               </ a >
           </ span >                           
           </ div >
           < div class = "userStoryDescription" >< div >{{ 说明 }}</ div >
      </ div >
</ div >
  
</ asp:Content >
  
< asp:Content ID = "PageTitle" ContentPlaceHolderID = "PlaceHolderPageTitle" runat = "server" >
应用程序页
</ asp:Content >
  
< asp:Content ID = "PageTitleInTitleArea" ContentPlaceHolderID = "PlaceHolderPageTitleInTitleArea" runat = "server" >
我的应用程序页
</ asp:Content >

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值