今さら言うのもなんですが、やったことをブログに書き起こしてみると自分がいかによくわからないままなんとな~く作っていたかがわかりますね・・・
(しかしそれでも動いてしまうのだから、システムって恐ろしい)
それはさておき、このシリーズの最後の投稿です。
前回View部分を定義しましたので、次にViewModelを定義します。
(このポストをおおいにパクって参考にしてます)
function Link(data) { this.SiteName = ko.observable(data.SiteName); this.SiteURL = ko.observable(data.SiteURL); this.SortOrder = ko.observable(data.SortOrder); this.ID = ko.observable(data.ID); } function LinkModel() { var self = this; self.Links = ko.observableArray([]); // get my personal link. $().SPServices({ operation: "GetListItems", async: false, webURL: ”MySiteのURL”, listName: "MyLinks", CAMLViewFields: "<ViewFields Properties='True' />", CAMLQuery: "<Query><OrderBy><FieldRef Name='SortOrder'/></OrderBy></Query>", completefunc: function (xData, Status) { var spsData = $(xData.responseXML).SPFilterNode("z:row").SPXmlToJson({ includeAllAttrs: true, removeOws: true }); if (spsData) { $.each(spsData, function (k, l) { var arrOrder = (l.SortOrder + "").split("."); self.Links.push(new Link({ SiteName: l.Title, SiteURL: l.SiteURL, SortOrder: arrOrder[0], ID: l.ID })) }); } // end if } // end complete func }); // end SPServices self.addSite = function() { self.Links.push({ SiteName: "", SiteURL: "", SortOrder: "", ID: "New" }); }; self.removeSite = function(data){ if (data.ID !== "New") { var batch = "<Method ID='1' Cmd='Delete'><Field Name='ID'>" + data.ID() + "</Field></Method>"; $().SPServices({ operation: "UpdateListItems", async: false, webURL: ”MySiteのURL”, listName: "MyLinks", updates: "<Batch OnError='Continue'>" + batch + "</Batch>", completefunc: function (xData, Status) { alert("completed"); } }); }; self.Links.remove(data); }; // end removeSites self.saveSites = function(){ var ret = self.Links(); var i = 1; var batch = ""; for(var n = 0, len = ret.length; n < len; n++){ if(ret[n].ID != "New"){ batch += "<Method ID='" + i + "' Cmd='Update'><Field Name='ID'>" + ret[n].ID() + "</Field><Field Name='SortOrder'>" + ret[n].SortOrder() + "</Field><Field Name='Title'>" + ret[n].SiteName() + "</Field><Field Name='SiteURL'>" + ret[n].SiteURL() + "</Field></Method>"; } else { batch += "<Method ID='" + i + "' Cmd='New'><Field Name='ID'>" + ret[n].ID + "</Field><Field Name='SortOrder'>" + ret[n].SortOrder + "</Field><Field Name='Title'>" + ret[n].SiteName + "</Field><Field Name='SiteURL'>" + ret[n].SiteURL + "</Field></Method>"; }/ i++; } $().SPServices({ operation: "UpdateListItems", async: false, webURL: ”MySiteのURL”, listName: "MyLinks", updates: "<Batch OnError='Continue'>" + batch + "</Batch>", completefunc: function (xData, Status) { alert("completed"); } }); }; // end saveSites } // end LinkModel
ここで、”observable”、”observableArray”っていうのが出てきますが、これは特殊な JavaScript オブジェクトで、ViewModelのプロパティの変更をViewに知らせることができます(逆もまたしかり)。”observable”は単一のオブジェクト、”observableArray”は配列に対し使います。
こう書くことでViewModel側でViewに表示させるべきデータが変われば自動的にViewが変わるので、スクリプト内でHTMLを書き換える必要がありません。
追加の場合はViewとバインドしてる配列に新しい要素を追加し、削除の場合は要素を削除、更新の場合は配列の中身をwebサービスでしぇあぽにばばーっと送っているだけです。DOM要素は一切さわってません。
なお、observableについて詳しくは本家のドキュメントをみてください。
さいごに、ViewとViewModelを関連付けます。これは簡単。
$(document).ready(function () { ko.applyBindings(new LinkModel()); });
あら不思議、これでKnockout.jsをつかって自前画面からSharePointのリストを操作できるようになりました!
しかし心残りなのは、更新処理のときに差分ではなく全部更新になっていること。アイテム数が少なければ全とっかえでもいいですが、数が増えるとどうなんだ・・・という感じです。
差分をとるうまいやり方を思いつけず今回はこの形ですが、できれば差分更新にしたいところ。よいアイデアあれば教えてください。
あ、あと、エラーとか例外とかいっさい処理してませんのであしからず(笑)
結局よくわからなかったところは、Modelは出てこなかったけど?!ってことです(笑)
SharePointで作る場合、何をViewModelとし、何をModelとするのか、自分の中で整理がついてないので、今後の課題ですね・・・