Подтвердить что ты не робот

Прогрессивное улучшение с помощью KnockoutJS

Скажем, мы имеем следующие данные

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>

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

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

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

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

Любые другие идеи?

4b9b3361

Ответ 1

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

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 в теги script, чтобы они никогда не отображались в браузерах NoJS. Шаблоны NoJS отображаются как содержимое div на стороне сервера. Как только будет вырисован шаблон нокаута, содержимое div будет заменено шаблоном нокаута в тегах script. Вы можете стилизовать их одинаковыми/похожими способами, чтобы сделать переход бесшовным, и если это невозможно, скройте шаблон noJS с помощью CSS.

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

Ответ 2

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

Ответ 3

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

Ответ 4

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

Прогрессивное расширение с бесконечным прокруткой KnockoutJS

screenshot

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

ko.bindingHandlers.PE = {
    init: function(element, valueAccessor, allBindings) {
        var bindings = allBindings();
        if (typeof bindings.text === 'function') {
            bindings.text($(element).text());
        }
    }
};

Ответ 5

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

Ответ 6

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