Как стать автором
Обновить

Директива добавления разделителей для ng-repeat + поддержка последнего разделителя, отличного от основного

Зачастую нам приходится отображать данные, хранящиеся в массиве на сайте. Для отображения строковых данных мы, как правило, разделяем их запятой. В случае обыкновенной строки идеальным вариантом было бы простое использование JS функции Array.join(', '). Но что делать в случае, если нам необходимо отобразить более сложные структуры? В большинстве случаев естественным выбором будет использование директивы AngularJS — ngRepeat.

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

Основные свойства директивы


scope: false — не конфликтует с другими директивами
priority: 1001 — на 1 выше чем у ngRepeat, отрабатывается раньше

Использование


Для того, чтобы использовать директиву, необходимо добавить на элемент, содержащий ng-repeat — атрибут ng-repeat-delimiter. В качестве параметра указать разделитель в скобках. Например ng-repeat-delimiter="(, )". // Первый, Второй, Третий, Четвёртый

Если указать второй разделитель: ng-repeat-delimiter="(, )( и )" (cкобки нужны для того, что бы сохранить пробелы), он будет использован в качестве последнего. // Первый, Второй, Третий и Четвёртый.

Вот Plunker с примером использования.

angular.module('ng.helpers').directive('ngRepeatDelimiter', ngRepeatDelimiter);

    function ngRepeatDelimiter() {
        return {
            restrict: 'A',
            priority: 1001, // на 1 выше чем у ng-repeat
            compile: compile
        };

        function compile(element, attrs){
            var delimAttr = attrs.ngRepeatDelimiter;

            var re = /\((.[^)]+)/g;
            var m;

            var delimiters = [];

            // забираем из атрибута первые 2 разделителя
            while ((m = re.exec(delimAttr)) !== null && delimiters.length < 2) {
                delimiters.push(m[1]);
                if (m.index === re.lastIndex) {
                    re.lastIndex++;
                }
            }

            if(delimiters.length == 0) return;

            var delimiter = delimiters[0];
            var lastDelimiter = delimiters[1];

            // идём от обратного, добавляя разделитель в начало, если не первый элемент и, 
            // если есть второй разделитель - не последний элемент
            var html = '<span ng-if="!$first' + (lastDelimiter ? ' && !$last' : '') + '">' + delimiter + '</span>';

            // если есть второй разделитель
            if(lastDelimiter){
                // добавляем второй разделитель, если не первый и последний
                html += '<span ng-if="$last && !$first">' + lastDelimiter + '</span>';
            }

            // заменяем исходный шаблон, убираем лишние пробелы
            var newTemplate = html + element.html().trim();

            element.html(newTemplate);
        }
    }

Директива подставляет в шаблон ngRepeat тег span с разделителем, который скрывается или отображается посредством директивы ngIf. На мой взгляд это наиболее чистое решение.

Если у вас есть идеи, как можно улучшить данное решение, прошу комментировать.

Спасибо.
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.