Drupal Tips: The Magic of '#attached'

Written on January 29, 2020

Category: Drupal Tips

Author: David Rodríguez, @davidjguru

Picture from Unsplash, by @gaspanik
Picture from Unsplash, user Masaaki Komori @gaspanik

Since a few days ago, I’m preparing a tutorial about the integration of JavaScript in Drupal that will be released soon (it will be written in Spanish in Medium). On the other hand, recently I was preparing some Drupal patches for the portability to Drupal 8 of a small contrib module, the humans.txt. Okay, but what do the two activities have in common?…


This article is part of a series of posts about Drupal Tips.

1- Drupal Fast Tips (I) - Using links in Drupal 8
2- Drupal Fast Tips (II) - Prefilling fields in forms
3- Drupal Fast Tips (III) - The Magic of ‘#attached’
4- Drupal Fast Tips (IV) - Xdebug, DDEV and Postman
5- Drupal Fast Tips (V) - Placing a block by code
6- Drupal Fast Tips (VI) - From Arrays to HTML
7- Drupal Fast Tips (VII) - Link Fields from Twig


The answer is simple: in both cases I had to make use of the #attached property, available for use in Drupal’s render arrays. And this made me think of a property available in the Drupal arrays that I still write about a little bit, and it’s very interesting. Don’t expect a big meditation, just some ideas and tips that I hope will be useful to someone. But first, let’s start at the beginning (or almost).

Retrospective

In some occasions, you may need add some resources to your Drupal project, in order to alter the rendering of some page, block or some stuff. In this case, you could add your resources in the Drupal 7 way, using some functions. You used to do the next steps using different procedural functions:

Adding HTML Meta tag: drupal_add_html_head()

Adding JavaScript: drupal_add_js()

Adding CSS: drupal_add_css()

Current Usage

But now in Drupal 8 and onwards, all is centralized in the #attached property. Well, no. It’s really based on two concepts that centralize everything in a shared way:

  1. The definition of “library”.
  2. The use of the property #attached.

First, you have to know what’s the format for libraries for your modules. Here you can see a set of examples defining libraries for custom modules

Second, you have to know the property. Using this property, you’ll be able to add some stuff, describe in the sub-properties of #attached. Using these sub-properties, you’ll can inform to the Render System of Drupal about the resources that you wanna use. It could be stuff like libraries, transported values from Server (PHP) to Client (JavaScript) using drupalSettings, and more things like diverse HTML tags.

We’re talking about some concepts like:

  • Library -> Libraries defined in a name_module.libraries.yml file, for CSS or/and JavaScript resources.
    Format: $render_array[‘#attached’][‘library’]

  • drupalSettings -> It’s an internal tool oriented to transport values from a multidimensional array in PHP to the JavaScript code.
    Format: $render_array[‘#attached’][‘drupalSettings’]
  • Http_Header -> Use this when add HTTP headers or / and status response codes. Format: $render_array[‘#attached’][‘http_header’]

  • HTML Link in Head -> Just for write more <link> tags in the HTML <head> section.
    Format: $render_array[‘#attached’][‘html_head_link’]

  • HTML Head -> Value to insert in case of tags in the HTML section <head>.
    Format: $render_array[‘#attached’][‘html_head’]

  • Feed -> Only for add classical RSS feeds.
    Format: $render_array[‘#attached’][‘feed’]

  • Placeholders -> Playing with placeholders in texts.
    Format: $render_array[‘#attached’][‘placeholders’]

  • HTML Response Placeholders -> Put placeholders in the HTML responses.
    Format: $render_array[‘#attached’][‘html_response_attachment_placeholders’]

Here you can see the defined allowed types, from a diff looking for unsupported types:
Supported keys for the attached property in Drupal 8

Supported keys for the attached property, from the Drupal.org official documentation.

For More Info, Visit: HtmlResponseAttachmentsProcessor

Examples

How can we modify some sections in our rendered HTML code from Drupal 8? For example, insert a new tag link in the <head> section for every rendered page…well, we’ll using the magical #attached property, such a key for our Drupal Render Arrays, ready to add some resources to our code.

In this case, I was trying to prepare a patch for the contrib module humans.txt and needed to insert a link in the HTML header pointing to the link.

The following example is not very optimized (the code can be reduced much more and I have to modify the sent patch) but this way it can be more didactic. It’s based on the use of the page_attachments_hook for Drupal. In the last line you can see the ‘attached’ property with the key ‘html_head_link’:

/**
 * Implements hook_page_attachments().
 */
function humanstxt_page_attachments(array &$attachments) {

    // Getting the current front path.
    $current_front_url = Url::fromRoute('<front>');
    $current_front_path = $current_front_url->toString();

    // Adding the filename to the path.
    $humanstxt_path = $current_front_path . "humans.txt";

    // Building the link data.
    $link_description = [
     'rel' => 'author',
      'link' => $humanstxt_path,
    ];

    // Adding the new head link tag.
    $attachments['#attached']['html_head_link'][] = [$link_description];
}

And here you can see the patch on its own issue, commented by Pedro Cambra from Cambrico http://cambrico.net/, a Drupal agency very focused on contributing and mentoring on Drupal.org.

Adding libraries in a render array

In the following case, it is only a matter of adding custom libraries of JavaScript resources to a render array returned by a controller method. A controller that ends up “painting” a simple data table through a path, and that is dynamized through custom libraries with Vanilla JavaScript.

// Before:
$final_array['welcome_message'] = [
  '#type' => 'item',
  '#markup' => $this->t('Hello World, I am just a text.'),
];
// After: 
$final_array['welcome_message'] = [
  '#type' => 'item',
  '#markup' => $this->t('Hello World, I am just a text.'),
  '#attached' => [
    'library' => [
      'javascript_custom_module/js_hello_world_console',
    ],
  ],
];

Passing values from PHP to JavaScript: drupalSettings

Other important use of the #attached property is passing values from PHP code to JavaScript in a standarized and formal way within a Drupal installation.

First, you will set the values from PHP In this case, we wanna load some special values from the current user account in order to transfer it and getting from the JavaScript side:

// We're adding the new resources to the same welcome element.

$final_array['welcome_message']['#attached']['drupalSettings']['data']['name'] = $this->current_user->getDisplayName();

$final_array['welcome_message']['#attached']['drupalSettings']['data']['mail'] = $this->current_user->getEmail();

Second, you will recover the values from JavaScript Now, we’ll get the values from the JavaScript side, using it for show an alert (for example):

(function () {
  'use strict';

  // Recovering the user name and mail from drupalSettings.
  let element = document.getElementById("salute");
  let user_name = drupalSettings.data.name;
  let user_mail = drupalSettings.data.mail;

  // Add to the HTML the new strings.
  element.innerHTML += "Update-> You are the user: " + user_name +
                       " with mail: " + user_mail;

})();

As we can see, we can use the property at the same time of building the renders arrays or later, through some hook to modify elements. What do you think?Interesting? The truth is that despite its simplicity and discretion, it’s something I almost use in all Drupal-based projects.

I hope you like it as well.

Greetings!

:wq!


Written on January 29, 2020