Асинхронные шаблоны в Knockout.JS

в 19:35, , рубрики: async, javascript, knockoutjs, template, метки: , ,

Knockout.JS — хорошая библиотека для создания сложных веб-приложений. Долгое время мне в ней не хватало асинхронного механизма шаблонов. Реализовать его не получалось, пока я не узнал что window.setTimeout вызывает свой callback не раньше окончания работы текущего контекста. Т.е. в коде

setTimeout("console.log(window.Value)",0),(function (){while (Math.random() < 0.9999999);window.Value = 1;})()

вывод на консоль произойдет только после завершения долгой функции случайного поиска числа очень близкого к единице.
Статья для разбирающихся в механизме биндинга knockout.js и умеющих писать customBindings.

Итак, зная поведение setTimeout(callback, 0) реализация очень простая.
Код custom-binding:


ko.bindingHandlers['asynctemplate'] = {
    update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        $(element).empty();
        var template = ko.utils.unwrapObservable(valueAccessor());
        if (!template)
            return;
        setTimeout(function() {
            $.ajax({
                url: template
            }).done(function(result){
                var view = $(result).appendTo(element)[0];
                ko.applyBindings(bindingContext.$data, view);
            });
        }, 0);
    }
}

Применение:

// хардкод путь к темлэйту
<div data-bind="asynctemplate: '/Templates/Controls/Components/Modal.html'"></div>
// или биндинг на переменную
<div data-bind="asynctemplate: templatePath"></div>
// нельзя одновременно с with или foreach
<div data-bind="asynctemplate: templatePath, with: templateViewModel">!ОШИБКА!</div>

Без нулевого timeout возникли бы проблемы: если получение шаблона займет небольшое время, то вставка шаблона в дерево DOM может произойти раньше, чем механизм биндинга knockout доберется до соответствующего элемента. В этом случае биндинг произойдет дважды, что ничем хорошим не заканчивается.

Эту проблему можно было бы решить другим способом: создать observable поле template, в которое сначала ставится затычка вроде «loading...», а уже при получении ответа с сервера загружается сам шаблон. Но это провоцирует мигания в интефейсе: на полсекунды появляется индикация загрузки.

Если кого-то заинтересует, могу добавить кэширование шаблонов и поддержку биндинга asynctemplate одновременно с with и foreach.

Автор: fransua

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js