Pull to refresh

Repeatable, еще один способ рендерить списки

Reading time4 min
Views5.2K
(из серии «малая механизация web страниц»)

Что такое Repeatable?


Repeatable это способ вывода (популяции) всякого рода списков, таблиц и пр. по массивам данных. Данный механизм
использует шаблон описанный в самом коде разметки (в отличие от, скажем, {{mustache}} templates).

Поддерживаются выражения и условное включение. И всё это в 90 строках кода.

Repeatable функиональность есть в каждом «взрослом» web framework'е. Но если вы не хотите по тем или иным причинам завязываться с монстрами то вот вам механизм который, что называется, есть не просит.

Пример


Скажем есть такие данные:
var data = [
       { name: "Olga", age: 20, email: "aaa@example.com" },
       { name: "Peter", age: 30, email: "bbb@example.com" },
       { name: "Ivan", age: 15, email: "ccc@example.com" },
    ];

И нам нужно из вывести как-то так:
<ul id="people">
    <li><a href="mailto:{{this.email}}">{{this.name}}</a> <b if="this.age > 18">18+</b> </li> 
    <li>No data available</li>
</ul>

Первый <li> собственно и есть шаблон записи. Для каждой записи во входном наборе этот элемент будет повторен с подстановками и гуляшшыми девами. Второй <li> будет выведен если Repetable «накормить» пустым массивом.

Если у нас это все описано то собственно популяция нашего списка это одна строка:
var list = $("ul#people").repeatable(); // declaring the repeatable
    list.value = data; // that's data population, sic!

Вот живой пример.

Микроформат шаблонов



Текст внутри разметки или значение аттрибута может содержать выражение в «mustache» скобках: {{ ...expr ...}}.
При заполнении списка такие выражения будут вычислены и замещены их строковым значениями.
Специпльные переменные доступные в выражениях

  • this — объект — текущий элемент списка.
  • $index — число, индекс текущего элемента списка;
  • $firsttrue если это первый элемент;
  • $lasttrue если это последний элемент;
  • $length — число, кол-во записей во входном массиве.

Условное включение

Любой элемент внутри repeatable шаблона может быть объявлен как условный. Для этого нужно описать у него атрибут if="...expr...". При генерации списка выражение будет вычислено и если оно «truthy» то элемент будет выведен, если нет — удален.

Домашняя страница repeatable plugin — здесь
Исходник repeatable привожу здесь полностью для тех кто пропустил ссылку в начале:
/**
* @author Andrew Fedoniouk <andrew@terrainformatica.com>
* @name jQuery repeatable()
* @license WTFPL (http://sam.zoy.org/wtfpl/)
* @purpose template-less population of repeatables (lists)
*/

(function ($) {

  function repeatable(el) {
        
    var $el = $(el);
    var template = $el.find(">*").remove();
    var nrTemplate = template.length > 1 ? $(template[1]) : null; // "no records" template
        template = $(template[0]);
    
    var compiled = {}; // compiled expressions
    var vector = null; // data
    var index = 0;     // current index being processed

    //function evalExpr(str) { return eval("(" + str + ")"); }
    function compiledExpr(str) { 
      var expr = compiled[str]; 
      if( !expr )
        compiled[str] = expr = new Function("$index","$first","$last","$total", "return (" + str + ")"); 
      return expr;
    }

    function replace(text, data) {
      function subst(a, b) { 
        var expr = compiledExpr(b);
        var s = expr.call(data, index, index==0,index==vector.length - 1, vector.length); return s === undefined ? "" : s; 
      }
      return text.replace(/{{(.*)}}/g, subst);
    }

    function instantiate(el, data) {
      var attributes = el.attributes;
      for (var i = 0; i < attributes.length; ++i) {
        var attribute = attributes[i];
        if (attribute.name == "if") {
          var str = attribute.value;
          var expr = compiledExpr(str);
          var tokeep = expr.call(data, index, index == 0, index == vector.length - 1, vector.length);
          if (!tokeep) { el.parentElement.removeChild(el); return; }
        }
        else if (attribute.value.indexOf("{{") >= 0)
          attribute.value = replace(attribute.value, data);
      }
      for (var nn, n = el.firstChild; n; n = nn) {
        var nn = n.nextSibling;
        if (n.nodeType == 1)  // element
          instantiate(n, data);
        else if (n.nodeType == 3) // text
        {
          var t = n.textContent;
          if (t.indexOf("{{") >= 0)
            n.textContent = replace(t, data);
        }
      }
    }

    function getValue() { return vector; }

    function setValue(newValue) {
      vector = newValue;
      var t = template[0];
      if( !vector || vector.length == 0 ) {
        $el.empty();
        if(nrTemplate)
          $el.append(nrTemplate); // no records
      }
      else {
        var fragment = document.createDocumentFragment();
        for (index = 0; index < vector.length; ++index) {
          var nel = t.cloneNode(true);
          instantiate(nel, vector[index]);
          fragment.appendChild(nel);
        }
        $el.empty();
        $el.append(fragment);
      }
    }

    el.getValue = getValue; el.setValue = setValue;    
    // redefine its 'value' property, setting value to some array will cause popupaltion of the repeatable by that data.
    try { Object.defineProperty(el, "value", { get: getValue, set: setValue, enumerable: true, configurable: true }); } catch(e) {}

    return el;
  }

  $.fn.repeatable = function () {
    var el = null;
    this.each(function () { el = repeatable(this); });
    return el; // returns last matched element!
  };

})(jQuery);


Успехов.
Tags:
Hubs:
+5
Comments5

Articles