Pull to refresh

Директива добавления разделителей для 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. На мой взгляд это наиболее чистое решение.

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

Спасибо.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.