2014-11-07 2 views
4

теряемся между возможностями, предоставляемыми обрабатывать этот случай: допустим, мы имеем следующие ограничения:Нокаут SPA Ajax нагруженных шаблонов

  • Нокаут
  • SPA с Sammy.js - Html, загруженным с помощью Ajax

Моя страница:

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

в настоящее время у меня есть один appViewModel, который обрабатывать д ata-bind для всех общих элементов моего сайта: #navigation и #sidebar. Этот appViewModel имеет наблюдаемое значение, используемое на всех страницах моего сайта.

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 - это часть DOM, загружаемая через Ajax Call. Каждый раз, когда пользователь нажимает ссылку внутри #navigation или #sidebar, Sammy.js перехватывает его, а затем обновляет #content. Проблема в том, что в новом DOM внутри #content есть привязки данных.

1) Во-первых, я должен использовать HTML данных привязку на #content, ReplaceWith (как указано выше) или шаблон связывания с пользовательской функции, чтобы получить шаблон? (http://knockoutjs.com/documentation/template-binding.html#note-5-dynamically-choosing-which-template-is-used)? Что такое лучшая практика здесь?

2) Должен ли Сэмми жить в appViewModel, как в документации, так и в другом месте, просто отлично?

3) Как только метод updateView будет завершен, как бы вы связали новый DOM? Как внизу? Разве нет риска перезаписи какого-либо DOM, потому что ko.applyBindings уже вызывается без второго аргумента?

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

Я благодарен вам за помощь.

+0

Поскольку это больше года, я хотел бы спросить, если вы когда-либо придумать решение? Я столкнулся с такой же точной дилеммой. – mls3590712

+0

Старый вопрос :-) Я закончил тем, что использовал нокаут AMD с requireJs и flatiron/Director в качестве маршрутизатора. Это работает как шарм. Использование этого стека создаст ограничения, которые помогут избежать этих вопросов. – Xavier13

ответ

0

Одно из простых решений заключается в том, чтобы сделать видимую модель страницы видимой и загрузить ее на задание. Используйте переменную для записи, если был вызван ko.applyBindings. Пример из рамок Нокаут-спа:

/*! 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 построен на вершине Нокаут, Требовать, директор JQuery, сахар. https://github.com/onlyurei/knockout-spa

Демо: https://knockout-spa.mybluemix.net