Когда вы впервые сталкиваетесь с директивой ngRepeat в AngularJS, это лучшая вещь после нарезанного хлеба - это чуть не взорвёт вашу голову.

В последнее время я много работаю с веб-компонентами и смотрю, как далеко вы можете зайти, создавая веб-приложения, не используя ничего, кроме веб-компонентов и ванильного JavaScript ES2015. Оказывается, довольно много (другая тема на другой день).

Поскольку я всегда так часто использовал ngRepeat в своих приложениях AngularJS, мне было любопытно реконструировать подобное поведение с помощью веб-компонентов. Поговорив об этом с Эриком Исаксеном (еще одним большим поклонником веб-компонентов) и увидев, насколько он заинтересован в том же, мне просто нужно было попробовать.

Процесс

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

  1. Предоставлять массив объектов компоненту через атрибут
  2. Укажите некоторую разметку, определяющую, как вы хотите, чтобы повторяющийся контент выглядел после рендеринга в DOM
  3. Используя какую-либо форму синтаксиса в шаблоне, укажите, какие значения из объектов вы хотите извлечь.

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

<rbl-repeater content="[{"firstName": "Bilbo", "lastName": "Baggins"}, {"firstName": "Frodo", "lastName": "Baggins"}]"></rbl-repeater>

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

<rbl-repeater>
    <div>
        <p>First Name: </p>
        <p>Last Name: </p>
    </div>
</rbl-repeater>

И для пункта три, поскольку я использовал ES2015, синтаксис для извлечения значений объекта должен соответствовать синтаксису строковой переменной шаблона:

<rbl-repeater>
    <div>
        <p>First Name: ${firstName}</p>
        <p>Last Name: ${lastName}</p>
    </div>
</rbl-repeater>

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

  1. Читать в массиве содержимого
  2. Возьмите HTML-код шаблона из его собственного внутреннего HTML-кода.
  3. Прокрутите массив содержимого и продублируйте шаблон для каждого объекта.
  4. Замените переменные шаблона фактическими значениями из текущего объекта
  5. Замените внутренний HTML новым созданным HTML.

Проблемы

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

<ul>
    <li ng-repeat="item in items">{{item.title}}</li>
</ul>

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

<li is="rbl-repeater" content='[{"title": "Game of Thrones"}]'>${title}</li>

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

document.registerElement("rbl-repeater", {
    prototype: RblRepeater.prototype,
    extends: 'ul'
});

И, насколько я понимаю, нет возможности «расширить» несколько типов элементов - по крайней мере, пока что.

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

<rbl-repeater element="ul" shadow="true" content='[{"title": "Game of Thrones"}]'>
    <li>${title}</li>
</rbl-repeater>

Не так хорошо, может быть, но достаточно хорошо :)

Окончательный кодекс

Вот окончательный код моего веб-компонента rebel-Repeater, который также можно найти на GitHub в разделе RevillWeb / rebel-Repeater.

Стоит отметить, что этот веб-компонент является скорее доказательством концепции, чем чем-то полностью полезным прямо сейчас, но я планирую в ближайшее время расширить его функциональность.

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

Удачного JavaScripting!

- Леон Ревилл