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

в 5:17, , рубрики: javascript, jquery, jquery plugin, веб-дизайн, Веб-разработка, метки: ,

(из серии «малая механизация 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);

Успехов.

Автор: csmile

Источник

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


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