Как вы используете собственные шаблоны 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)