Как вы используете собственные шаблоны Knockout с внешним механизмом шаблонов KO

Я использую внешний механизм шаблонов KO, чтобы разбить мое одностраничное веб-приложение на несколько файлов, но загружаемые шаблоны содержат только разметку KO.

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

Ключевыми моментами являются:

  • Элементы DOM, определенные шаблоном, изначально не существуют
  • Мои привязки шаблонов являются динамическими, поэтому я не хочу, чтобы KO запрашивал шаблоны при применении привязок ко всему документу
  • Я не хочу делать синхронизацию загрузки шаблона
  • Pb сделан еще более хитрым из-за того, что infuser, что может происходить некоторое кеширование шаблонов, что заставляет KO жаловаться, что я применяю привязки дважды.

Это код, который у меня есть, который имеет как минимум две проблемы:

  • Нет гарантии, что шаблон завершил загрузку, так как я звоню applyBindings
  • К.О. жалуется, что я пытаюсь повторно применить привязки, когда я перемещаюсь назад и вперед

Любое предложение для чистого, надежного метода применения привязок один раз и только один раз к внешним узлам шаблона, когда они добавляются в DOM?

  • В index.html:

    <div id="templateDiv" data-bind="template: { name: currentView() }"></div>
    
  • В main.js:

    function AdminViewModel() {
         var self = this;
         self.currentView = ko.observable('adminHome');
     }
    
    var viewModel = new AdminViewModel();
    
    var SammyApp = $.sammy('#admin_content', function() {
      //...
      this.get('#/editMembers', function(context) {
          viewModel.currentView('editMembers');
          ko.applyBindings(viewModel, $('.ko-template').get(0));
      });
    };
    
    ko.applyBindings(viewModel);
    

4 ответа

Если я вас понял, то вы можете сделать это

загрузить шаблон в один базовый файл в js, используя get и назначить тегу script, как показано ниже

var script   = document.createElement("script");
               script.id  = "YourTemplateName";
               script.type  = "text/html";
               script.text  = result.Value; //template data
               document.body.appendChild(script);

и в вашем html файле index/base назначьте шаблон

<div id="OtherTemplateDiv" data-bind="template: { name: 'YourTemplateName' }">
            </div>

Я сделал шаблон движка для нокаута

https://github.com/AndersMalmgren/Knockout.Bootstrap.TemplateStore/wiki

Для этого требуется веб-сервер с поддержкой Owin, после настройки он понимает, что ViewModel FooViewModel должен быть подключен к представлению под названием FooView

Установить с помощью nuget (для ASP.NET)

Install-Package Knockout.Bootstrap.TemplateStore.SystemWeb

Это также разработано, чтобы быть простым в использовании в SPA

Демо https://github.com/AndersMalmgren/Knockout.Bootstrap.Demo

Я придумал что-то, что работает с использованием обратного вызова afterRender... хотя ИМХО немного грязно, улучшения приветствуются.

По какой-то причине обратный вызов afterRender вызывается дважды, второй раз с пустым объектом, следовательно, проверяется hasOwnProperty ('nodeType').

isBound () проверяет, были ли привязки уже применены к элементу - попытка добавить собственный CSS-класс маркера ('ko-apply') к элементу после применения привязок не сработала надежно.

Не уверен, что копирование массива koElements действительно необходимо, но без этого я получил неопределенные элементы [i] в ​​цикле, поэтому загрузчик шаблонов может асинхронно обновлять массив во время работы afterRender.

<div id="templateDiv" data-bind="template: { name: currentView(), afterRender: applyTemplateBindings }"></div>

self.applyTemplateBindings = function(koElements) {
            var elements = koElements.slice();
            for (var i = 0, len = elements.length; i < len; i++) {
                var element = elements[i];
                if (element.hasOwnProperty('nodeType') && ! $(element).hasClass('infuser-loading') &&
                    ! isBound(element)) {
                    ko.applyBindings(self, element);
                }
            }
        };

        var isBound = function(node) {
            return !!ko.dataFor(node);
        };

Это не решение о том, как использовать шаблоны, но именно так я делю на файлы, используя привязку with.

Предположим, у вас есть такая страница:

<div data-bind="with: block1">
    <input data-bind="value: yourFirstInput" />
    <!-- more markeup -->
</div>
<div data-bind="with: block2">
    <select data-bind="options: dropDownlist2options"></select>
    <!-- more markeup -->
</div>

Вы можете поставить block1 в файле (я использую ascx), block2 в другом файле.
Тогда в вашей viewmodel у вас есть что-то вроде:

var viewmodel = function () {
    var self = this;
    this.block1 = ko.observable();
    this.block2 = ko.observable();
}
var vm = new viewmodel();
ko.applyBindings(vm);

Таким образом, вы применяете привязки на всей странице. with binding будет обрабатывать проверку на наличие неопределенных / неопределенных объектов и отображать блоки только при создании экземпляров объектов.

Затем вы можете сделать что-то подобное, когда вы хотите отобразить block1:

vm.block1({ yourFirstInput: ko.observable('aa')});

На самом деле я делаю это с плагином отображения, это всего лишь пример.

Демонстрация в реальном времени (обратите внимание, как block1 появляется после setTimeout)

Другие вопросы по тегам