1

I have a field on a node type that references another node type. On the display side, I am setting it to a view mode to be rendered.

What I want to do is add a zebra stripe class. Every other node needs an extra class on its wrapper. I figure this should be done at the field twig level, but how do you pass in a value when it is rendered?

field--field-related-content.html.twig:

{% for item in items %}
  {{ item.content }}
{% endfor %}

In the node view mode template:

<a class="hl__type-promo" href="{{ path('entity.node.canonical', {'node': node.id() }) }}" title="{{ node.getTitle() }}" style="background-image: url('../../assets/images/fpo_16x9.png')">
  <div class="hl__type-promo__header">
    <div class="hl__type-promo__icon">
      {% include "@mytheme/svg/general/how-to.html.twig" %}
    </div>
    <div class="hl__type-promo__subject">How-To</div>
  </div>
  <div class="hl__type-promo__details"><h3 class="hl__type-promo__title">{{ label }}</h3>
    <div class="hl__type-promo__description">
      DESCRIPTION HERE
    </div>
  </div>
</a>

The node is unaware of where it is, so, whats the best way to pass a class? Is that possible? The field.html.twig file in classy wraps the field with a div, but thats not what I need to do here.

I suppose one way to do it is change the field twig to something like:

{% for item in items %}
  {% set classes = '...' %}
  <a class="{{ classes }}" href="{{ path('entity.node.canonical', {'node': item.content['#node'].id() }) }}" title="{{ item.content['#node'].getTitle() }}" style="background-image: url('../../assets/images/fpo_16x9.png')">
    {{ item.content }}
  </a>
{% endfor %}

But that feels dirty, and doesn't really make it reusable, not to mention I would have to inject the background image url through a field preprocess instead of just handle it in the node rendering.

1
  • 1
    If you want to make the node aware of where it is placed you get more cache variations. For performance it is better to add the index to the field container and target the node and its elements by combining multiple css selectors or apply the zebra with :nth-child(even/odd) or a bootstrap class.
    – 4uk4
    Apr 6, 2018 at 19:20

1 Answer 1

2

I did something like this in Drupal 7 and did have caching issues as 4k4 mentioned, but I think the cache issues would be easier to avoid in D8. You could do this by creating a new Field Formatter that extends EntityReferenceEntityFormatter.

You should be able to alter just the viewElements() method and add the delta information to the referenced entities. However, this will create an issue with the view mode being cached with your delta info. For example, you may have an entity be the first item the first place it's referenced and the second item the second place it's referenced, but it'll be cached from the first call so your delta alterations won't work how you'd expect. To workaround this issue, you can update the #cache settings for the entities and add a new key for the delta.

I would start by calling parent::viewElements() and then make a pass through that result to make the alterations (adding the delta value and updating the cache settings).

I would still alter the field template for the parent EntityReference field and add the class to a wrapping div rather than on the entity itself. You could create your own template file and add your own theme suggestion to make it more reusable, something like field__zebra, rather than relying on the default theme suggestion you mentioned above, field__my_field_name. I love Display Suite so I would create a Zebra DsFieldTemplate that I could reuse on any field I wanted.

1
  • Great answer. I think caching should work if you add to #cache an additional cache key for the delta. Entities have already one for the view mode, then they would also vary by the delta.
    – 4uk4
    Apr 6, 2018 at 22:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.