Twig, добавить первый и последний класс

#php #symfony #twig

#php #symfony #twig

Вопрос:

Допустим, у меня есть цикл for, подобный этому:

 {% for elem in arrMenu %}
    <div class="topmenu-button">
        <a href="{{ elem.url }}">{{ elem.name }}</a>
    </div>
{% endfor %}
  

В этой форме это будет отображать что-то вроде:

 <div class="topmenu-button"><a href="url">name</a></div>
<div class="topmenu-button"><a href="url">name</a></div>
<div class="topmenu-button"><a href="url">name</a></div>
<div class="topmenu-button"><a href="url">name</a></div>
  

Как twig может помочь мне добавить первый и последний классы в div, чтобы у меня был результат, подобный:

 <div class="topmenu-button first"><a href="url">name</a></div>
<div class="topmenu-button"><a href="url">name</a></div>
<div class="topmenu-button"><a href="url">name</a></div>
<div class="topmenu-button last"><a href="url">name</a></div>
  

Комментарии:

1. Вы могли бы использовать :first-child и :last-child псевдоклассы. В наши дни они широко поддерживаются.

2. Вы также можете использовать IE7-js для добавления поддержки CSS3 в некоторые устаревшие IE.

3. Просто небольшое замечание; Я только что исправил ошибку, в которой я инвертировал параметры for .

Ответ №1:

Вы можете использовать «специальные переменные» loop.first и loop.last для того, что вы хотите.

Ссылка

 {% for elem in arrMenu %}
    {% if loop.first %}
    <div class="topmenu-button first">        
    {% elseif loop.last %}
    <div class="topmenu-button last">        
    {% else %}
    <div class="topmenu-button">        
    {% endif %}
        <a href="{{ elem.url }}">{{ elem.name }}</a>
    </div>
{% endfor %}
  

РЕДАКТИРОВАТЬ: в зависимости от того, насколько вы заботитесь об «одном случае», вам может понадобиться такое решение.

 {% for elem in arrMenu %}
    {% set classes = ["topmenu-button"] %}
    {% if loop.first %}{% set classes = classes|merge(["first"]) %}{% endif %}
    {% if loop.last %}{% set classes = classes|merge(["last"]) %}{% endif %}
    <div class="{{ classes|join(" ") }}">        
        <a href="{{ elem.url }}">{{ elem.name }}</a>
    </div>
{% endfor %}
  

Комментарии:

1. Возможно, я ошибаюсь, я новичок в Twig, но если массив содержит только один элемент if , оба оператора будут выполнены, что приведет к class="topmenu-button first last . В этом случае вы должны использовать if...else . Но, как я уже сказал, я могу ошибаться.

2. В общем, мы также можем использовать что-то вроде этого: последний ? ‘последний’ }} «>

Ответ №2:

Поскольку цикл не может быть first и last одновременно, я бы предпочел не использовать elseif и не писать (DRY — что, если вам придется меняться topmenu-button в будущем?):

 {% for elem in arrMenu %}
    {% set append %}
        {% if loop.first %}first{% endif %}
        {% if loop.last %}last{% endif %}
    {% endset %}
    <div class="topmenu-button {{ append }}">
        <a href="{{ elem.url }}">{{ elem.name }}</a>
    </div>
{% endfor %}
  

или более лаконичный:

 {% for elem in arrMenu %}
    <div class="topmenu-button {% if loop.first %}first{% endif %} {% if loop.last %}last{% endif %}">
        <a href="{{ elem.url }}">{{ elem.name }}</a>
    </div>
{% endfor %}
  

Редактировать: таким образом, вы получите несколько дополнительных пробелов в class атрибуте (кстати, это прекрасно).
Edit2: это не будет обрабатывать массив из 1 элемента (первый = последний)

Комментарии:

1. Раньше я пытался кодировать HTML (и twig) подобным образом, но, честно говоря, ваше «более краткое» решение выглядит намного более шумным, а в предыдущем примере больше кода для установки переменной, чем в основной части <div> вашей печати. Такой заманчивый язык, как TWIG, на самом деле не подходит для идеального соответствия DRY, поскольку весь «код» должен быть экранирован {% / %} .

2. @KendallHopkins Я согласен с тем, что вы говорите о сжатом коде, я всегда буду использовать первый. ИМХО, лучше написать больше кода для установки переменной, а не повторять div объявление и class атрибут 3 раза. С таким количеством повторений, как у вас, изменение чего-либо будет действительно болезненным (и пустой тратой времени и денег). Плюс elseif и else выглядит ужасно для меня.

3. Посмотрев на эту проблему еще раз, я думаю, что мы оба ошибаемся. Мы забываем «один» случай, когда вы можете иметь class="topmenu-button first last" . Учитывая эту дополнительную сложность, я думаю, что лучше всего будет обработать merge filter и разбить его на собственную переменную, аналогичную тому, как вы это делаете. Я обновил свой ответ, WDYT?

4. @KendallHopkins ты чертовски прав. 1 для вас, мне нравится ваш (второй) подход в вашей правке. Однако я бы инвертировал loop.first и loop-last проверил, поскольку первый и единственный элемент массива должен рассматриваться и стилизоваться как первый. Но это зависит от него.

Ответ №3:

Если это для управления атрибутом класса тега, троичное условие было бы лучше.

 <span class="topmenu-button {{ (loop.first) ? 'first' : '' }}">{{ item.text }}</span>
  

Ответ №4:

Я использую это в шаблоне представлений Drupal 8. Спасибо за «elseif», если у вас есть только 1 элемент, то первый элемент никогда не получит «последний» класс:

 {% for row in rows %}
    {% set parity = cycle(['odd', 'even'], loop.index0) %}
    {% set row_classes = [ default_row_class ? 'views-row', ] %}    

    <div class="views-prerow {{parity}}{% if loop.first %} first{% elseif loop.last %} last{% endif %}">
        <div{{ row.attributes.addClass(row_classes) }}>
            {{ row.content }}
        </div>
    </div>
{% endfor %}