knockoutjs 单页引述 客户端重定向(location.hash history.pushState)

knockoutjs 单页引用 客户端重定向(location.hash history.pushState)大部分现代的,相应的,迷人的Web应用

knockoutjs 单页引用 客户端重定向(location.hash history.pushState)
大部分现代的,相应的,迷人的Web应用已经超越经典的Ajax,变成单页引用(single page applications:)。用户能象使用本地应用一样,在一个界面内完成各种操作,一个很著名的例子就是:GMail。

这些单页引用使用hash-based url 或者 pushState 导航来支持 浏览器的回退、前进、书签操作。(location.hash就是#锚点,一般用在Web页面内部的片段之间进行导航!)

data-bind="text: $data": $data表示引用foreach循环中的数组数据

data-bind="with: chosenFolderData": with表示后面的变量chosenFolderData不是null时,才使用绑定的相关控件。with内部的所有控件绑定变量时,引用就不需要再加chosenFolderData.前缀了,直接使用mails就可以

chosenFolderId, chosenFolderData , chosenMailData 都是被观察对象,在相关操作时给这些变量设置, 然后一些绑定到这些变量的UI会被自动更新。chosenFolderId是被观察者,所有取值、设置都是通过方法的形式做的

client-side navigation客户端重导航,使用hash-based url 或者 pushState技术。Sammy 就是使用hash-based url。

Sammy js client-side navigation的基本技术是采用一层额外的indirection客户端内部定向。没有使用之前,goToFolder 和goToMail 方法都是直接调用Ajax请求然后更新viewmodel 的状态。使用后,goToFolder 和goToMail 方法仅仅触发client-side navigation客户端导航,然后Sammy 包会捕获到客户端导航操作再发出相应的Ajax请求后再更新viewmodel状态。indirection客户端内部定向也就意味着如果用户点击浏览器上面的回退、前进按钮,Sammy会捕获到这些请求,然后viewmodel 会被正常更新

location是javascript里边管理地址栏的内置对象,比如location.href就管理页面的url,用location.href=url就可以直接将页面重定向url。一个完整的URL地址的格式为:协议://主机:端口/路径名称#hash标识?搜索条件,
而location.hash则可以用来获取或设置页面的标签值。比如http://domain/#admin的location.hash="#admin"。

<!-- Todo: Create UI --><script src="/scripts/lib/sammy.js" type="text/javascript"></script><!-- Folders --><ul data-bind="foreach: folders">    <li data-bind="text: $data,               css: { selected: $data == $root.chosenFolderId() },               click: $root.goToFolder"></li></ul><!-- Mails grid --><table data-bind="with: chosenFolderData">    <thead><tr><th>From</th><th>To</th><th>Subject</th><th>Date</th></tr></thead>    <tbody data-bind="foreach: mails">        <tr data-bind="click: $root.goToMail">            <td data-bind="text: from"></td>            <td data-bind="text: to"></td>            <td data-bind="text: subject"></td>            <td data-bind="text: date"></td>        </tr>         </tbody></table><!-- Chosen mail --><div data-bind="with: chosenMailData">    <div data-bind="html: messageContent" /></div>

function WebmailViewModel() {    // Data    var self = this;    self.folders = ['Inbox', 'Archive', 'Sent', 'Spam'];    self.chosenFolderId = ko.observable();    self.chosenFolderData = ko.observable();    self.chosenMailData = ko.observable();    // Behaviours    //self.goToFolder = function(folder) {    //    self.chosenFolderId(folder);    //    self.chosenMailData(null); // Stop showing a mail    //    $.get('/mail', { folder: folder }, self.chosenFolderData);    //};    //self.goToMail = function(mail) {    //    self.chosenFolderId(mail.folder);    //    self.chosenFolderData(null); // Stop showing a folder    //    $.get("/mail", { mailId: mail.id }, self.chosenMailData);    //};    self.goToFolder = function(folder) { location.hash = folder };    self.goToMail = function(mail) { location.hash = mail.folder + '/' + mail.id };        // Show inbox by default    // self.goToFolder('Inbox');        // Client-side routes        Sammy(function() {        this.get('#:folder', function() {            self.chosenFolderId(this.params.folder);            self.chosenMailData(null);            $.get("/mail", { folder: this.params.folder }, self.chosenFolderData);        });        this.get('#:folder/:mailId', function() {            self.chosenFolderId(this.params.folder);            self.chosenFolderData(null);            $.get("/mail", { mailId: this.params.mailId }, self.chosenMailData);        });                this.get('', function() { this.app.runRoute('get', '#Inbox') });    }).run();};ko.applyBindings(new WebmailViewModel());