所謂的用戶端物件模型就是調用了背後的WCF服務來提供資料,為了減輕資料的訪問量資料包使用JSON,我們還可以看到物件模型的設計也加入了諸多對於減輕資料訪問量的考量。技術上沒有什麼新意,你要願意,在SharePoint2007裡面也可以實作類別似的功能,當然在使用上方便了我們不少
三種使用用戶端模型的.NET託管、ECMA指令碼,SilverLightClient.
本文講闡述如何使用.NETManaged 程式碼來訪問SharePoint物件模型。
ECMAScript Client OM需要注意的幾個點
- ECMAScript僅能夠在SharePoint網站裡面使用,不能夠在其他的Asp.NET網站裡使用ECMAScript來訪問SharePoint網站資源,也不能夠跨SharePoint網站訪問資源;
- JQuery和ECMAScript使用起來不會有衝突;
- 為了安全的更新內容,在使用ECMAScript的畫面裡添加<SharePoint:FormDigest runat="server" />
- 在隨後你將會看到的代碼裡為了減輕載入的資料量,可以指定需要載入的內容,例如client.Context.load(this.web,'Title','Id','Created'), 這裡的屬性值名稱使用和CAML一樣的體系,對大小寫敏感;
- 為確保你的代碼執行在SP.JS載入完之後再被調用,可以使用ExecuteOrDelayUntilScriptLoaded(myjsFunction, “sp.js”)。
我們看看SharePoint OM和用戶端OM的一個簡單的匹配關係:
伺服器端OM |
用戶端OM |
SPContext |
ClientContext |
SPSite |
Site |
SPWeb |
Web |
SPList |
List |
SPListItem |
ListItem |
SPField |
Field |
看看最後會呈現的效果,是初步計劃的功能,主要設計列表的建立、查詢以及管理,另外也涉及上傳檔案的Case,後續裡如果有重要的也會逐步加進來。
裡面的連結會調用UI方面的Javascript介面建立SharePoint2010風格的快顯視窗,快顯視窗的後台頁面位於SitePage文件庫內,請注意這個僅僅適用於開啟的頁面是WebPart page,如果不是開啟的時候會報錯誤:“The Ribbon Tab with id: "Ribbon.Read" has not been made available for this page or does not exist”。
(注意,此Page在之後都不會被用到,留在這裡僅為瞭解釋Ribbon用)
建立列表:
首先,通過Designer,加入以下兩個Script連結:
<SharePoint:ScriptLink Name="SP.js" runat="server" OnDemand="true" Localizable="false" />
<SharePoint:ScriptLink Name="SP.debug.js" runat="server" OnDemand="true" Localizable="false" />
ECMAScriptOM和.NET Managed ClientOM(隨後會講到)異曲同工,但也有幾點需要注意的:
- 在ClientContext裡面不能使用伺服器端URLs;
- 不支援LINQ;
- 本質上ECMAScript OM是非同步
代碼非常的簡單易懂,裡面有個好玩的東西SP.UI.Notify.addNotification,通過這個類可以在調用的畫面裡顯示提示訊息,非常的SharePoint。
示範結果如下:
在文字框裡輸入列表名字後,點擊"Create List”按鈕,產生列表後會在右上方提示“List test1 created”,本例中使用annoucement做為清單類型。
原始碼如下:
複製代碼 代碼如下:
<script type="text/javascript">
var messageId;
function createList(listName){
var clientContext = new SP.ClientContext();
var oWebSite = clientContext.get_web();
var listCreationInfo = new SP.ListCreationInformation();
listCreationInfo.set_title(listName);
listCreationInfo.set_templateType(SP.ListTemplateType.announcements);
listCreationInfo.set_quickLaunchOption(SP.QuickLaunchOptions.on);
var oList = oWebSite.get_lists().add(listCreationInfo);
clientContext.load(oList);
clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
}
function onQuerySucceeded() {
//Remove the 'creating' event notification
if(messageId != null)
{
SP.UI.Notify.removeNotification(messageId);
}
//Add 'created' notification as non sticky
messageId = SP.UI.Notify.addNotification("List <b>" + oList.get_title() + "</b> created...", false, "", null);
}
function onQueryFailed(sender, args) {
//Remove the 'creating' event notification
if(messageId != null)
{
SP.UI.Notify.removeNotification(messageId);
}
//Shown in case of error on the JS OM call
messageId = SP.UI.Notify.addNotification("Operation was cancelled...", false, "", null);
}
</script>
擷取所有列表:
同樣,先看一下效果,點擊“Get All List”按鈕,會將當前網站下的所有列表都讀取出來並設定了響應的超連結屬性,點擊“Hide List”按鈕則將之隱藏(實際上就是個Div)
代碼非常的直接,只說明一個點,getEnumerator()以及moveNexst(), get_current()等JavaScript函數的使用為遍曆集合提供了很好的方法。
原始碼:
複製代碼 代碼如下:
function getLists(){
var clientContext = new SP.ClientContext();
var oWebSite = clientContext.get_web();
listCollection = oWebSite.get_lists();
clientContext.load(listCollection);
clientContext.executeQueryAsync(Function.createDelegate(this, this.onGetListsSucceeded), Function.createDelegate(this, this.onGetListsFailed));
}
function onGetListsSucceeded(){
var str = "";
var listsEnumerator = listCollection.getEnumerator();
while(listsEnumerator.moveNext()){
var objList = listsEnumerator.get_current();
str += "<a href='" + "http://localhost" + objList.get_parentWebUrl() + objList.get_defaultViewUrl() + "'>" + objList.get_title() + "</a>" + "<br/>";
}
document.getElementById("lists").innerHTML = str;
}
function onGetListsFailed(sender, args){
alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
CAML查詢:
這裡面提供了兩種方式查詢,一個按DueDate,一種按Title,當然功能可以設計的更加有利於使用者使用些,Demo裡就不做過多渲染。點擊Search就可以進行資料的查詢,有一點小發現,如果使用<asp:calendar/>控制項的時候選擇好日期會導致頁面postback,SharePoint裡面至少有兩種解決方案:
- 單獨在某個頁面裡面放入calendar控制項,然後添加以下代碼:
<input type="text" id="txtDate" name="txtDate" />
<button value="lookup" onclick="document.all['txtDate'].value =
window.showModalDialog( 'Calendar.aspx' );" >
使用SharePoint Calendar控制項<SharePoint:DateTimeControl runat=server id="DateTimeControl1" DateOnly="True"></SharePoint:DateTimeControl>
做了一段對控制項顯示的控制,選擇Date則出現輸入Date的控制項,選擇Title則出現輸入Title的控制項,本來想用JQuery的方法,後來一下沒想起來JQuery的Selector寫法,半土不洋的用了下面的方法結合控制:
複製代碼 代碼如下:
<script type="text/javascript">
function changeQueryMethod(){
var method = $("select[id='selectQueryMethod']").val();
if(method == 'Title'){
document.getElementById('querybytitle').style.display = "inline";
document.getElementById('querybyDate').style.display = "none";
}
else{
document.getElementById('querybytitle').style.display = "none";
document.getElementById('querybyDate').style.display = "inline";
}
}
</script>
關於CAML查詢的細節本身也不做過多說明,如果有興趣可以參見拙文(http://www.cnblogs.com/johnsonwong/archive/2011/02/27/1966008.html),這是一篇針對2007版本的CAML,在2010裡有了很多增強,譬如跨列表Joint查詢等,隨後會發布相應2010的版本。
需要注意的知識是:
裡面使用了對field的查詢,注意相關API的調用;
ClientContext裡面對若干個結果集進行操作,但需要調用Load對不同結果集進行載入:clientContext.load(fieldCollection);clientContext.load(listItemCollection);
如果有需要讀取的欄位值需要在CAML查詢XML裡面顯示說明,否則不會返回到結果集裡,這也是出於對效能的考慮
複製代碼 代碼如下:
function search(){
var clientContext = new SP.ClientContext();
var oWebSite = clientContext.get_web();
var list = oWebSite.get_lists().getByTitle("Tasks");
fieldCollection = list.get_fields();
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml( "<View><Query><Where><Gt>" +
"<FieldRef Name='DueDate' />" +
"<Value Type='DateTime'>2008-01-1T00:00:00Z</Value>" +
"</Gt></Where></Query><ViewFields>" +
"<FieldRef Name=\"Title\" /><FieldRef Name=\"Body\" />" +
"<FieldRef Name=\"DueDate\" />" +
"</ViewFields></View>");
listItemCollection = list.getItems(camlQuery);
clientContext.load(fieldCollection);
clientContext.load(listItemCollection);
clientContext.executeQueryAsync(Function.createDelegate(this, this.onSearchListSucceeded), Function.createDelegate(this, this.onSearchListFailed));
}
function onSearchListSucceeded(){
var str = "";
var listItemEnumerator = listItemCollection.getEnumerator();
var fieldsEnumerator = fieldCollection.getEnumerator();
while(listItemEnumerator.moveNext()){
var oListItem = listItemEnumerator.get_current();
str += "Item " + oListItem.get_id() + ":"
while(fieldsEnumerator.moveNext()){
var oField = fieldsEnumerator.get_current();
str += oField.get_staticName() + "<br/>";
}
str += "<br/>";
}
document.getElementById("lists").innerHTML = str;
}
function onSearchListFailed(sender, args){
alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
操作檔案:
遺憾的是在ECMAScript裡面無法上傳檔案,雖然有SP.File對象,但更多的是對擷取回來的SP.File對象進行操作。