Problem/Motivation

There is no easy way to add an attribute (class, title, rel, target) to the anchor tag of link field unless it is of the "Separate title and URL" format. No template is provided for the default format.

From field.html.twig template:

<div{{ attributes.addClass(classes) }}>
  {% if not label_hidden %}
    <div{{ title_attributes.addClass(title_classes) }}>{{ label }}</div>
  {% endif %}
  <div{{ content_attributes.addClass('field-items') }}>
    {% for item in items %}
      <div{{ item.attributes.addClass('field-item') }}>{{ item.content }}</div>
    {% endfor %}
  </div>
</div>

item.content contains the anchor tag of a link field.

From template_preprocess_field:

  $variables['items'] = array();
  $delta = 0;
  while (!empty($element[$delta])) {
    $variables['items'][$delta]['content'] = $element[$delta];

    // Modules (e.g., rdf.module) can add field item attributes (to
    // $item->_attributes) within hook_entity_prepare_view(). Some field
    // formatters move those attributes into some nested formatter-specific
    // element in order have them rendered on the desired HTML element (e.g., on
    // the  element of a field item being rendered as a link). Other field
    // formatters leave them within $element['#items'][$delta]['_attributes'] to
    // be rendered on the item wrappers provided by field.html.twig.
    $variables['items'][$delta]['attributes'] = !empty($element['#items'][$delta]->_attributes) ? new Attribute($element['#items'][$delta]->_attributes) : clone($default_attributes);
    $delta++;
  }

$variables['items'][$delta]['attributes'] is rendered on the outer div <div{{ item.attributes.addClass('field-item') }}>

So the only possible way is to write a hook_entity_prepare_view like rdf module does. Since themes can't add this hook, you have to write a custom module:


/**
 * Implements hook_entity_prepare_view().
 */
function my_module_links_block_entity_prepare_view($entity_type_id, array $entities, array $displays, $view_mode) {
  if ($entity_type_id == 'some_entity_type') {
    foreach ($entities as $entity) {
      if ($entity->bundle() == 'some_bundle') {
        foreach ($displays[$entity->bundle()]->getComponents() as $name => $options) {
          if ($name == 'some_link_field') {
            foreach ($entity->get($name) as $item) {
              $item->_attributes += array('class' => array('Yay-add-my-class'));
            }
          }
        }
      }
    }
  }
}

Proposed resolution

Move https://www.drupal.org/project/link_attributes to core

Remaining tasks

Add tests
Updating exisitng config in post update hook
Review

User interface changes

Link widget will allow to set attributes from ui.

API changes

None

Files: 

Comments

jibran’s picture

From LinkFormatter::buildUrl()

    $url = $item->getUrl() ?: Url::fromRoute('');

    $settings = $this->getSettings();
    $options = $item->options;

    // Add optional 'rel' attribute to link options.
    if (!empty($settings['rel'])) {
      $options['attributes']['rel'] = $settings['rel'];
    }
    // Add optional 'target' attribute to link options.
    if (!empty($settings['target'])) {
      $options['attributes']['target'] = $settings['target'];
    }
    $url->setOptions($options);

    return $url;

url options can be added using $item->options but there is no UI for that in field settings page.

Mac_Weber’s picture

Issue summary: View changes

@jibran maybe we just need to add it to the UI settings? I've changed the issue summary accordingly.

rootwork’s picture

It would be really fantastic to have these in the UI (class, rel and target).

Is this still possible in the RC phase? If not I guess it should be postponed to 8.1.x, but maybe in the meantime we could document the custom module route -- my guess is a lot of themers are going to want to add classes to their links.

rootwork’s picture

Thinking about this more, is there a reason this couldn't be moved to a template like the other old field preprocess functions were? There's already a link-formatter-link-separate.html.twig template; if the functionality to render a link field could be moved to a template too, then themers could add attributes like class, title, rel, target etc. pretty easily.

rootwork’s picture

Title: Improve TX of link field; No easy way to add class to anchor tag of a link field » Create link-formatter.html.twig template to allow for easy changes to attributes (class, title, rel, target) on outputted link fields
Issue tags: +Twig conversion

I read more from #1898426: link.module - Convert theme_ functions to Twig and I'm really confused why there's the link-formatter-link-separate.html.twig template but nothing for a (non-separated) link. If the theming functions for theme_link_formatter_link_separate were converted, why not for regular links? In fact link_theme only even mentions link_formatter_link_separate.

Bizarre. I'm changing the title to try to get some feedback on my suggestion that we have a template for themed link fields, or at least some explanation of why that doesn't exist.

rootwork’s picture

Updated issue summary with clearer explanation; fixed a couple typos.

rootwork’s picture

mstrelan’s picture

url options can be added using $item->options but there is no UI for that in field settings page.

Can you give a specific example of how / where to add this? Can this be done in hook_preprocess_field()?

rootwork’s picture

Version: 8.0.x-dev » 8.1.x-dev

I take it that's a question for jibran.

Using the opportunity to move this to 8.1.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.0-beta1 was released on March 2, 2016, which means new developments and disruptive changes should now be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

weseze’s picture

There seems to be some progress in contrib modules, but neither of them are working for me...
https://www.drupal.org/project/link_class
https://www.drupal.org/project/link_class_widget

The only working solution I found to this problem was to add a preprocess function in my theme!

function MYTHEME_preprocess_field(&$variables) {
  if ($variables['field_name'] == 'field_MYFIELD') {
    foreach ($variables['items'] as $delta => $item) {
      $variables['items'][$delta]['content']['#options']['attributes']['class'][] = 'MYCLASS';
    }
  }
}

I also tried in a field twig, but I could not figure out how to access the attributes array from there.

Is anything happening to include basic things like this in the Drupal core Link Formatter? Is there any reason this was not included?

rootwork’s picture

@weseze Leah Wagner just wrote this up. You're right that currently the only way is to use a preprocess function, but her suggestion is a little more flexible:

In your THEME.theme file:

function THEME_preprocess_field(&$variables, $hook) {
  // Make additional variables available to the template.
  $variables['bundle'] = $variables['element']['#bundle'];
}

And then in your theme's field.twig.html:

{% extends "@stable/field/field.html.twig" %}

{# Create classes array #}
{% set classes = [
  bundle ~ '__' ~ field_name|replace({'field_' : ''})|clean_class
] %}

{% set attributes = attributes.addClass(classes) %}

This is extending the Stable theme's field template, but you could also extend the Classy theme instead. This is also applying it to all fields, but you could apply it just to link fields with a field-specific template. And this is programmatically adding classes based on the field name; if you want to just apply a specific class, you'd prepend (or replace) the bundle... line with the quoted class, i.e. 'MYCLASS'.

That said, the original suggestion in this issue -- to make adding all attributes (including not just classes but things like title and rel) possible with a core link-field Twig template -- would be more flexible (and more understandable) still. So that's what I'm pulling for.

jibran’s picture

Title: Create link-formatter.html.twig template to allow for easy changes to attributes (class, title, rel, target) on outputted link fields » Update LinkWidget to accommodate the attribute.
Category: Task » Feature request
Issue summary: View changes
Status: Active » Needs review
FileSize
3.05 KB

Status: Needs review » Needs work

The last submitted patch, 13: 2477155-12.patch, failed testing.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Jehu’s picture

what about title attribute?