Когда вы впервые сталкиваетесь с директивой ngRepeat в AngularJS, это лучшая вещь после нарезанного хлеба - это чуть не взорвёт вашу голову.
В последнее время я много работаю с веб-компонентами и смотрю, как далеко вы можете зайти, создавая веб-приложения, не используя ничего, кроме веб-компонентов и ванильного JavaScript ES2015. Оказывается, довольно много (другая тема на другой день).
Поскольку я всегда так часто использовал ngRepeat в своих приложениях AngularJS, мне было любопытно реконструировать подобное поведение с помощью веб-компонентов. Поговорив об этом с Эриком Исаксеном (еще одним большим поклонником веб-компонентов) и увидев, насколько он заинтересован в том же, мне просто нужно было попробовать.
Процесс
Когда я сказал, что должен подумать, это было на самом деле довольно простой концепцией.
- Предоставлять массив объектов компоненту через атрибут
- Укажите некоторую разметку, определяющую, как вы хотите, чтобы повторяющийся контент выглядел после рендеринга в DOM
- Используя какую-либо форму синтаксиса в шаблоне, укажите, какие значения из объектов вы хотите извлечь.
В первом пункте не было особой нужды, учитывая, что добавление 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>
После этого компонент должен выполнить следующие шаги при запуске:
- Читать в массиве содержимого
- Возьмите HTML-код шаблона из его собственного внутреннего HTML-кода.
- Прокрутите массив содержимого и продублируйте шаблон для каждого объекта.
- Замените переменные шаблона фактическими значениями из текущего объекта
- Замените внутренний 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!