標籤:des style blog http java color
門戶視圖
隨著 Timecard 列表的增多,如何尋找和管理這許多的 Timecard 也就成了問題。尤其對於團隊經理而言,他除了自己填寫的 Timecard,還要審核團隊成員的 Timecard 任務更重。
這裡我把實際的需求簡化成為 2 個主要的視圖(但能夠提供的效果和實際需求其實是非常接近的):
- Time Window 視圖
這個視圖列出目前使用者在所有可以填寫的時間視窗中是否提交了 Timecard,起到提醒的作用。
- Timecard 視圖
這個視圖列出在 Timecard 網站中,所有目前使用者參與(包括提交和審批 Timecard)的項目/組織中的 Timecard 的審批狀態。方便瞭解自己的 Timecard 填報進展、方便團隊經理尋找還沒有審批的 Timecard。
技術方案
2個門戶視圖有很多種技術方案來實現。
- Content Query Web Part 或者 Content Search Web Part。實現第二個視圖還勉強可以,注意,只是勉強。實現第一個視圖需要 Group Count 功能,直接歇菜。Pass。
- 可視化 Web Part。C# 開發,伺服器(場)部署。可以利用伺服器端的緩衝技術來提高效能。不過,調試是個噩夢,不信你可以搬著指頭數數你重啟一次 Web Front 需要多少秒。
- Sandbox Web Part。C# 開發,網站部署。具有上面伺服器陣列方案的優點外也避免了它的缺點。不過 SharePoint 2013 叫大家不要用。
- SharePoint Hosted App。JavaScript 開發,伺服器(場)發布、網站部署。需要額外配置一個專用的 app 網域名稱和認證。其實這個方案不錯,擴充性也很好。但是相比下面的方案,實現顯得複雜了點兒。(另外,如果有時間折騰,其實 Provider Hosted App 也可以考慮,這個甚至允許你用 PHP 來寫。是那些不喜歡(不願意喜歡)SharePoint 而又不得不用 SharePoint 的人的不二之選)。順帶提一句,所有 App 方案都有一個巨大初始的優勢:調試。直接用 VS 調 JavaScript,那是相當喜聞樂見的。為什麼說是“初始”的優勢呢?因為一旦基本的 SharePoint 操作你都調得差不多了(熟練掌握)的時候,這個優勢就慢慢消失了,那個時候,你的 JavaScript 代碼其實已經不容易出現無法在 failure 函數裡面捕獲的錯誤了。
- Embedded JavaScript,在網頁上面嵌 JavaScript 代碼。實現起來最簡單,最好維護,對伺服器端壓力也最小,不刷屏,保護視力。就以上門戶視圖的需求來看,我選擇這個方案。而且,這個方案的代碼,搬到 SharePoint Hosted App 也不浪費的。
技術實現
JavaScript (SharePoint CSOM)開發,最煩的是其非同步回調的機制。所有向伺服器發送的操作,都要在回呼函數裡面收結果,然後你才能繼續下一步的商務邏輯。
目前我找到的比較容易減輕這個癥狀的方案,是用 Deferred。先將調用伺服器的操作用 Deferred 封裝,然後用 $.when 來調用並捕獲其返回結果。這樣,至少形式上看上去,後續的商務邏輯操作是緊跟在前面一步的伺服器調用後面的,看上去就舒服多了。
也有很多 JavaScript 的函數庫提供了這個問題的解決方案,但在試用之後,我都放棄了。簡單的問題還是用簡單的方案吧。
為了說明這個實現方法,我們先看一個空的 Deferred 封裝的 SharePoint 調用:
1: return $.Deferred(function (dtd) {
2: var web = context.get_web();
3: sp.context.load(web);
4: var failure_callback = Function.createCallback(onSharePointFailed, dtd);
5: sp.context.executeQueryAsync(
6: function () {
7: var title = web.get_title();
8: dtd.resolve();
9: },
10: Function.createDelegate(this, failure_callback));
11: }).promise();
上面的例子中,context 是在調用前初始化好了的 SharePoint Context 全域變數,而 onSharePointFailed 則是預先定義的出錯回呼函數。
通過上面這樣的形式,就完成了一個最簡單的 Deferred 封裝。
為了使用上面的封裝,我們先要將其放入一個函數中去:
1: var spGetWeb = function () {
2: var web = context.get_web();
3: context.load(web);
4: var failure_callback = Function.createCallback(sp.Failed, dtd);
5: sp.context.executeQueryAsync(
6: function () {
7: var title = web.get_title();
8: dtd.resolve();
9: },
10: Function.createDelegate(this, failure_callback));
11: }).promise();
12: }
好了,下面就可以開始調用了(這隻是個例子,真的要調用,還是要做很多準備的,比如初始化 context 等等):
1: $.when(spGetWeb())
2: .done(function(){
3: message.succeed("Web is ready.");
4: $.when(spGetList("Time Window"))
5: .done(function(){...})
6: .fail(function(){...});
7: })
8: .fail(function(){
9: message.error("Can‘t get the web.");
10: }}
上面的例子中,先調用了 spGetWeb,在成功以後(done),接著又調用了 spGetList。這樣,原先像麵條一樣散落的回調商務邏輯,可以用比較人性化的方式呈現了,我們也好少死幾個腦細胞。
下面的視頻是實現的效果: