【问题标题】:Knockout SPA Ajax loaded templatesKnockout SPA Ajax 加载的模板
【发布时间】:2015-01-04 12:39:36
【问题描述】:

我在处理这种情况的可能性之间迷失了:假设我们有以下限制:

  • 淘汰赛
  • 带有 Sammy.js 的 SPA - 通过 Ajax 加载的 Html

我的页面:

+-------------------------------+
| #navigation                   |
+---------+---------------------+
| #sidebar|    #content         |
|         |                     |
|         |                     |
|         |                     |
+---------+---------------------+

目前,我有一个 appViewModel 来处理我网站的所有 shared 元素的数据绑定:#navigation 和 #sidebar。这个 appViewModel 在我网站的每个页面上都使用了 observable。

appViewModel = function () {
    var self = this;
    self.sidebarItemArray = ko.observableArray([x, y, z]);
    self.currentRoute = ko.observable();

    ...

    self.updateView = function(path, currentRoute) {
        return $.get(path, function( data ) {
            var $data = $(data);
            // Updates #content, TITLE and update the currentRoute observable.
            $( '#content' ).replaceWith($data.find('#content'));
            document.title = $data.filter('title').text();
            self.currentRoute(currentRoute);
            }, 'html');
    }

    Sammy(function() {
        this.get(':link'', function() {
            self.updateView(this.path, this.params.link);
        });
    }).run();
}
ko.applyBindings(new appViewModel());

现在,假设#content 是通过Ajax 调用加载的一段DOM。每次用户单击#navigation 或#sidebar 中的链接时,Sammy.js 都会拦截它,然后更新#content。问题是 #content 中的新 DOM 本身具有数据绑定。

1) 首先,我应该在#content 上使用 html 数据绑定、replaceWith(如上)还是使用 template 绑定获取模板的自定义函数? (http://knockoutjs.com/documentation/template-binding.html#note-5-dynamically-choosing-which-template-is-used)? 这里的最佳做法是什么

2) Sammy 是否应该像文档中或其他地方一样在 appViewModel 中生存就可以了?

3) 一旦 updateView 方法完成,你将如何绑定新的 DOM?像下面这样?是否存在重新绑定某些 DOM 的风险,因为 ko.applyBindings 已经在没有第二个参数的情况下被调用?

ko.applyBindings(new routeSpecificViewModel() , document.getElementById("content")); 

感谢您的帮助。

【问题讨论】:

  • 这已经有一年多了,我想问一下您是否提出过解决方案?我也面临着同样的困境。
  • 老问题 :-) 我最终使用带有 requireJs 和 flatiron/director 作为路由器的 Knockout AMD。这就像一个魅力。使用此堆栈将创建约束,从而避免这些问题。

标签: ajax knockout.js sammy.js


【解决方案1】:

一个简单的解决方案是使页面的视图模型成为可观察的,并按需加载。使用变量来记录是否调用了 ko.applyBindings。来自 knockout-spa 框架的示例:

/*! knockout-spa (https://github.com/onlyurei/knockout-spa) * Copyright 2015-2016 Cheng Fan * MIT Licensed (https://raw.githubusercontent.com/onlyurei/knockout-spa/master/LICENSE) */

define(['app/shared/root-bindings', 'framework/page-disposer', 'ko', 'sugar'], function (
  RootBindings, PageDisposer, ko) {

  var initialRun = true;

  var Page = {
    init: function (name, data, controller, path) {
      Page.loading(false);

      name = name.toLowerCase();

      if ((Page.page().name == name) && (Page.page().data == data)) { // if the requested page is the same page, immediately call controller without going further
        if (controller) {
          controller(data);
        }

        document.title = Page.title();

        if (Page.initExtra) {
          Page.initExtra(name, data, controller);
        }

        return data;
      }

      var autoDispose = (Page.page().data.dispose && Page.page().data.dispose(Page)) || true; // if the requested page is not the same page, dispose current page first before swap to the new page
      if (autoDispose !== false) {
        // auto-dispose page's exposed observables and primitive properties to initial values. if not desired, return
        // false in dispose function to suppress auto-disposal for all public properties of the page, or make the
        // particular properties private
        PageDisposer.dispose(Page.page().data);
      }

      PageDisposer.init(data); //store initial observable and primitive properties values of the page
      var initialized = (data.init && data.init(Page)) || true; // init view model and call controller (optional) before template is swapped-in
      if (initialized === false) {
        return false; // stop initialization if page's init function return false (access control, etc.)
      }
      if (controller) {
        controller(data);
      }

      Page.pageClass([name, ('ontouchstart' in document.documentElement) ? 'touch' : 'no-touch'].join(' '));
      Page.page({
        name: name,
        data: data,
        path: path
      }); // to test if template finished rendering, use afterRender binding in the template binding

      document.title = Page.title();

      if (Page.initExtra) {
        Page.initExtra(name, data, controller); // useful for common init tasks for all pages such as anaylitics page view tracking, can be set in RootBindings
      }

      if (initialRun) {
        ko.applyBindings(Page, document.getElementsByTagName('html')[0]); // apply binding at root node to be able to bind to anywhere
        initialRun = false;
      }

      return data;
    },
    page: ko.observable({
      name: '', // name of the page - auto-set by the framework, no need to worry
      data: {
        init: function () {}, // preparation before the page's template is rendered, such as checking access control, init/instantiate modules used by the page, etc.
        dispose: function () {} // properly dispose the page to prevent memory leaks and UI leftovers (important for SPA since page doesn't refresh between page views) - remove DOM element event listeners, dispose knockout manual subscriptions, etc.
      }
    }),
    pageClass: ko.observable(''),
    loading: ko.observable(false),
    title: function () {
      return Page.page().name.titleize(); // override in RootBindings as needed
    }
  };

  Object.merge(Page, RootBindings); // additional root bindings as needed by the app

  return Page;

});

一个迷你但成熟的 SPA 框架,构建在 Knockout、Require、Director、jQuery、Sugar 之上。 https://github.com/onlyurei/knockout-spa

现场演示:https://knockout-spa.mybluemix.net

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-06
    • 2018-01-21
    • 1970-01-01
    • 2018-01-22
    • 2017-12-04
    • 2016-05-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多