Drupal Tips: The Magic of '#attached'
Written on January 29, 2020
Category: Drupal Tips
Author: David Rodríguez, @davidjguru
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()
-
$meta_charset = array(‘#tag’ => ‘meta’, ‘#attributes’ => array(‘charset’ => ‘utf-8’));
-
drupal_add_html_head($meta_charset, ‘meta_charset’);
Adding JavaScript: drupal_add_js()
-
drupal_add_js(‘http://cdn.jquerytools.org/1.2.6/jquery.tools.min.js’, ‘external’);
Adding CSS: drupal_add_css()
-
drupal_add_css(‘http://fonts.googleapis.com/css?family=News+Cycle’, array(‘type’ => ‘external’));
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:
- The definition of “library”.
- 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’]- Read more about the integration between JavaScript and Drupal in the related guide: How to integrate JavaScript in Drupal 8 - 9.
-
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, 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.
Adding a new <link> in the <head> HTML Tag
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;
})();
- Read more about the topic in the reffered guide for integration of JavaScript in Drupal: www.therussianlullaby.com/how-to-integrate-javascript-in-drupal-8-9/.
- Read more about the Render system in Drupal in the official documentation from Drupal.org: api.drupal.org/theme.api/group/theme_render.
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!