Прогрессивное улучшение с KnockoutJS - PullRequest
19 голосов
/ 22 января 2012

Допустим, у нас есть данные следующим образом

var data = {
  facets: [{
    name : "some name",
    values: [{
      value: "some value" 
    }]
  }]
};

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

<ul data-bind="foreach: facets">    
  <li>      
    <span data-bind="text: name"></span>
    <ul data-bind="foreach: values">            
      <li data-bind="text: value"></li>     
    </ul>
  </li>
</ul>

Вопрос в том, какМожем ли мы достичь того же результата при использовании прогрессивного улучшения?Это значит, что шаблон должен отображаться на стороне сервера изначально, а затем привязывать шаблон вывода и модель представления к этому отображению.

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

<ul>    
  <li>      
    <span>some name</span>
    <ul>            
      <li>some value</li>       
    </ul>
  </li>
</ul>

Я исследовал несколько различных возможностей:

  • Один из них - создать один шаблон выбивки и один шаблон на стороне сервера и динамически сгенерировать модель представления Knockout, анализируя DOM для шаблона на стороне сервера.,Таким образом, при включенном JavaScript будет виден только шаблон Knockout, а при отключенном JavaScript будет виден только шаблон на стороне сервера.Они могут быть разработаны таким образом, чтобы они выглядели одинаково.

  • Другой подход заключается в применении привязок для каждого элемента в массиве фасетов отдельно к существующему элементу DOM для этого фасета.Однако это все еще только один уровень глубины и не работает для вложенных элементов.

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

Есть еще идеи?

Ответы [ 6 ]

4 голосов
/ 28 января 2012

Я исследовал несколько подходов, в том числе создание анонимного шаблона из первого элемента, как описано здесь:

http://groups.google.com/group/knockoutjs/browse_thread/thread/3896a640583763d7

или создание отдельных привязок для каждого элемента массива с помощью настраиваемой привязки, например

ko.bindingHandlers.prerenderedArray = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
        var binding = valueAccessor();              
        binding.firstTime = true;

        $(element).children().each(function(index, node) {                  
            var value = ko.utils.unwrapObservable(binding.value)[index];                        
            ko.applyBindings(value, node);
        }); 

        return { 'controlsDescendantBindings': true };              
    },
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {             
        var binding = valueAccessor();
        if (binding.firstTime) {
            binding.firstTime = false;
            return;
        }               

        $(element).children().remove(); 
        ko.applyBindingsToNode(element, { template: { name: binding.template, foreach: binding.value }}, viewModel)
    }
};      

, который применяет определенную привязку к каждому элементу, а затем при первом обновлении заменяет содержимое обычной привязкой foreach. Этот подход все еще означает, что вам все еще нужно иметь два шаблона Оба из них также включают инициализацию состояния через JSON, отображаемый сервером.

Текущий подход, который я выбрал (все еще подлежащий изменению), заключается в том, чтобы поместить все шаблоны Knockout в теги сценария, чтобы они никогда не отображались в браузерах NoJS. Шаблоны NoJS отображаются как содержимое div на стороне сервера. Как только шаблон Knockout будет обработан, содержимое тега будет заменено шаблоном Knockout в тегах сценария. Вы можете стилизовать их одинаковыми / похожими способами, чтобы сделать переход плавным, а если это невозможно, скрыть шаблон noJS с помощью CSS.

В конце концов, я пришел к выводу, что Knockout.js и прогрессивное улучшение на самом деле не очень хорошо работают вместе, нужно выбрать другой, то есть создать некоторые части приложения, которые требуют прогрессивного улучшения, используя более традиционные методы. такие прямые манипуляции с JQuery DOM.

3 голосов
/ 22 января 2012

Боюсь, нет чистого способа сделать это. Лично я отрисовываю страницу в бэкэнде, а затем передаю те же самые данные контекста во внешний интерфейс (сериализованный как JSON) и настраиваю Knockout, используя его. Это означает, что есть некоторое дублирование. Возможно, переключение серверной части на node.js упростит ситуацию.

3 голосов
/ 22 января 2012

Просто добавьте различные атрибуты data- в шаблоны на стороне сервера.Они не причиняют вреда, если JavaScript отключен, поэтому их наличие вовсе не проблема.

1 голос
/ 26 сентября 2014

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

Прогрессивное улучшение с KnockoutJS Infinite Scroll

screenshot

Прогрессивное связывание с улучшением

ko.bindingHandlers.PE = {
    init: function(element, valueAccessor, allBindings) {
        var bindings = allBindings();
        if (typeof bindings.text === 'function') {
            bindings.text($(element).text());
        }
    }
};
0 голосов
/ 23 июля 2014

Прогрессивное улучшение намного проще с нецикличными данными, такими как формы (как я писал здесь: http://www.tysoncadenhead.com/blog/using-knockout-for-progressive-enhancement). Лично я считаю, что зацикливание нескольких элементов в DOM и повторное их рендеринг кажется сложной реализацией, но сложно придумать что-нибудь лучше.

0 голосов
/ 25 августа 2013

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

...