D8 JS Fundamentals, 8.4 News & Beyond

Todd Zebert @toddzebert

drupal.org/u/todd-zebert

Lead Web Dev, Miles @meetmiles

v2.2.0

D8 Integrations

From Not-Invented-Here to Proudly-Found-Elsewhere

Drupal 8 includes many "best of breed" integrations: Symfony, Twig, PSR-0, Composer and more, but notably these JavaScript libraries:

  • Underscore.js - functional programming helpers
  • Backbone.js ... but no one's using - it will probably eventually go away

We won't cover those in this presentation

Loading JavaScript in D8

Drupal 8 now exclusively uses a "libraries" concept. This is a conceptual extension of D7's hook_library() and the D7 Libraries API Module.

Libraries are defined in YAML format, of course.

You can define one or more libraries in each file.

Libraries are also used for CSS but we'll ignore that for this discussion.

Define a Library

Create or edit the <some-file-name>.libraries.yml file that goes in the root of your theme or module:


this-lib-name:
  js:
    js-path/my-theme.js: {}
						

some-file-name is the machine name, and should be the name of your theme or module.

Drupal will aggregate JS files, so if not needed:


    js-path/my-theme.js: { preprocess: false }
						

js-path is relative to the module folder. See PSR-4 namespaces and autoloading in Drupal 8 for more info on pathing.

Library options

You can optionally provide a version number per library - Drupal doesn't use it for anything:


this-lib-name:
  version: "1.0.1"
						

You can also define dependencies per library:


this-lib-name:
  dependencies:
    - core/jquery
    - some_module/some_lib
						

D8 by default loads JS into the footer; to header:


this-lib-name:
  header: true
						

Attaching Libraries in a Theme

Reference them in your my-theme-name.info.yml file. They load globally as in on every page.


libraries:
  - core/jquery
  - my-theme-name/this-lib-name
						

Here we're loading our libraries this-lib-name and core/jquery (which is no longer loaded by default).

Attaching Libraries in Twig

You can attach a JS library from a twig template. It will only load if the template loads which means you can conditionally load it based on the naming structure of the Twig template file.


{{ attach_library('my-theme-name/some-other-lib') }}
						

Attaching Libraries in Module PHP

PHP gives you a high degree of control about what libraries are loaded and when.


function mymodule_page_attachments(array &$attachments) {
  $attachments['#attached']['library'][] =
    'mytheme_or_module/some-other-lib';
}
						

Of course you can load any number of libraries in this, and related hooks, that affect the render array.

Attaching Libraries in Theme PHP

If not loading everywhere by using the .yml file.


function mytheme_preprocess_page(&$variables) {
  $variables['#attached']['library'][] =
    'mytheme_or_module/some-other-lib';
}
						

See also related THEME_preprocess_HOOK()'s. Although some discourage using these hooks as they're intended for preprocessing variables.

Conditional Libraries in PHP

To support D8's caching, if you want to *conditionally* attach libraries, you need to provide cacheability metadata*.


function mytheme_or_module_preprocess_page(&$variables) {
  $variables['page']['#cache']['contexts'][] = 'url.path';
  // above line sets the cacheability metadata

  if (\Drupal::service('path.matcher')->isFrontPage()) {
    $variables['#attached']['library'][] =
      'mytheme_or_module/some-other-lib';
  }
}
						

*This is comprised of a combination of up to 3 things: tags, contexts, and max-age. See the Cache API https://www.drupal.org/developing/api/8/cache for more info.

Loading External JavaScript

External "libraries" still have to go inside Drupal libraries.


this-lib-name:
  js:
    https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.js:
      { type: external }

External libs may also have a number of other optional "meta" properties.

Warning: I "broke" the lines for readability, but it's not legal.

Loading External JavaScript, cont.

Since Drupal will minify, if it already is, declare that it is:


{ type: external, minified: true }
						

If attributes are needed in the resulting script line:


      { type: external , attributes: { defer: true, async: true } }

JavaScript Settings

To add "computed" settings or configuration, first you have to define a dependency on core/drupalSettings.


this-lib:
  dependencies:
    - core/drupalSettings
						

function mytheme_preprocess_page(&$vars) {
  $vars['#attached']['library'][] = 'mytheme/this-lib';
  $vars['#attached']['drupalSettings']['mytheme']
    ['this-lib']['some-prop'] = $some_var;
}
						

Then access the some-prop setting in JS with:


console.log(drupalSettings.mytheme.this-lib.some-prop);
						

Manipulating Libraries

The theme is able to use two new directives, libraries-extend and libraries-override to extend, remove or replace whole libraries or individual library files.

Both of these are placed in the theme's my-theme.info.yml file.

libraries-extend


libraries-extend:
  other_theme_or_module/some-lib:
    - mytheme/some-other-lib
						

Here, the some-other-lib of the theme is used to extend some-lib of some mytheme_or_module

By "extending" a library it means the new library is always loading with the target library, regardless of conditions.

libraries-override

This directive is more powerful, but when dealing with JS (unlike CSS which may just result in poor layout/design), you can break JavaScript on your site by not paying close attention to JS code dependencies.

You can remove libs:


libraries-override:
  other_theme_or_module/some-lib: false
						

libraries-override, cont.

Or removal of a library file:


libraries-override:
  other_theme_or_module/some-lib:
    js:
      full-path-to-library/some-file.js: false
						

Replacement of libs:


libraries-override:
  other_theme_or_module/some-lib: mytheme/some-other-lib
						

Or replacement of a library file:


libraries-override:
  other_theme_or_module/some-lib:
    js:
      full-path-to-library/some-file.js: path-to-theme-lib/some-other-file.js
						

Inline JavaScript

Almost always JavaScript should be included in a library.

But if you must, put the complete script tag in the html.html.twig file.

Dynamically Altering Libraries

Use this when you want to dynamically specify JS (or CSS) Libraries used across multiple requests.

hook_library_info_alter(&$libraries, $extension)

Allows modules and themes to change libraries' definitions

An example is the Color module which replaces CSS Libraries with a colored CSS libraries.

Dynamically Adding Libraries

To programmatically add an entire library definition this is the hook for you. And they are still cached.

Modules may implement hook_library_info_build() to add dynamic library definitions:

function my_module_library_info_build() {
  $libs = array();
  $libs['my-lib'] = [
    'js' => [
      'my-lib-code.js' => [],
    ],
  ]
}
						

Sneaky JS

While you're suppose to use libraries you can attach into HTML head and create a script tag directly. These are (re)built on every request and so aren't cached and can slow down Drupal, so beware.

If possible, it's best to leave your JS file(s) static and just use JS Settings configurations


$page['#attached']['html_head'][] = [
  [
    '#type' => 'html_tag',
    '#tag' => 'script',
      '#attributes' => [ 'src' => 'some-path/my-file.js' ],
  ], 'my-mode-js', // an identifier for Drupal
];
						

That covers all the ways to get JavaScript into your Drupal sites - what you do with it is a different matter.

Core Libs

Core libs defined: core/core.libraries.yml. Examples, all prefixed with "core\":

  • drupal
  • drupalSettings
  • drupal.autocomplete*
  • drupal.debounce
  • drupal.tabledrag
  • jquery
  • jquery.joyride
  • jquery.ui
  • jquery.ui.autocomplete*
  • underscore

* Note that many jQuery libs are "wrapped" by Drupal libs. Best to use the Drupal lib.

What's new with Drupal 8.4.0 and JS?

Brief highlights:

Browser Compatibility

Internet Explorer 9 and 10 are no longer supported (woo-hoo!)

IE 9 and 10 MS support ended April 2017

Browser share (July '17):

  • IE 8: 0.93%
  • IE 9: 0.68%
  • IE 10: 0.52%
  • IE 11*: 12%

IE11 shipped with Windows 8.1 which has a EOL of January 10, 2023 :(

jQuery Updated

Versions:

  • to: 3.2.1 (March '17 - current, 84.6KB)
  • from: v2.2.4 (May '16, 99.6KB)

Correspondingly, jQueryUI and other minor libs were updated also.

V3 upgrade guide (June '16) - there's breaking changes jquery.com/upgrade-guide/3.0/

JS-Related Testing

PHPUnit has a patch-level updates.

"Many former WebTestBase [currently 242 via Simpletest] tests were converted to BrowserTestBase [currently 998 functional via PHPUnit]"

Check the progress on Simpletest Countdown simpletest-countdown.org

REST/API

  • "Authenticated REST API performance increased by 15% by utilizing the Dynamic Page Cache"
  • POSTing entities can now happen at /node, /taxonomy/term and so on, instead of /entity/node, /entity/taxonomy_term
    • Backward compatibility maintained
  • Time fields now are normalized to RFC3339 timestamps by default
  • Other minor changes

REST/API part 2

Allow direct REST endpoint for resetting a user's password:

  • route: user.pass.http
  • path: '/user/password'
  • method: POST JSON
  • controller: \Drupal\user\Controller\
    UserAuthenticationController::resetPassword

AirBnB JavaScript Guide

Airbnb JavaScript style guide [v14.1] adopted for both core and contrib

ES6 Support

ECMAScript 2015 aka JavaScript 6 (Jun '15)

While not mentioned in the release notes, see "Use ES6 for core JavaScript development" www.drupal.org/node/2809281

There's no rewrite to use newer features and syntax, but an effort underway to correct syntax to pass the AirBnB (ES6) linter.

ES7 was finalized June 2016, ES8 June 1017, and ES9 likely June 2018

ES6 Support, part 2

ES6 Support (Kangax compatibility table):

  • IE11 11%
  • Edge 12-15 60-95%
  • FireFox 45-56 85-97%
  • Chrome 53+ 97%
  • Safari 10+ 99%
  • iOS9-11 53-99%

ES6 Support, part 3

You only need the following if maintaining core JS:

So, while if dealing with anything "new" you're probably OK with sending native ES6 code, but Drupal uses Babel* (for core JS only) to maintain compatibility back to the likes of IE11:

Uses scripts/js/babel-es6-build.js to "compile *.es6.js files to ES5." **

* Requires Node, and installation of dependencies - Yarn is recommended.
** There's also a script to "watch".

Looking Forward to 8.5 and beyond

drupal.org/core/roadmap

  • 8.5: March 7, 2018
  • 8.6: September 5, 2018
  • 8.7: March, 2019

8.5, JavaScript Testing

    As of 8.1 JavaScript testing is available via PHPUnit (with Phantom.js).

    There's a proposal to move from PHPUnit Functional JS testing to using JS based testing drupal.org/node/2869825

  • Nightwatch.js "Write End-to-End tests in Node.js quickly and effortlessly that run against a Selenium/WebDriver server" nightwatchjs.org

8.5-8.6, Continue "API First"

drupal.org/project/ideas/issues/2757967

  • Continue to improve REST's DX, like revisions and UUID support, etc. See drupal.org/project/relaxed
  • "Should Have"
    • JSON API in core?
    • GraphQL into core?
  • "Could Have"
    • CouchDB support
    • OAuth 2.0 Bearer Token into core?

8.7, React? The facts

  • "Drupal looking to adopt React" - Dries, 10/2/17
  • Proposed during DrupalCon Vienna
  • Important that this is only targeted for admin UI to improve UX
  • Facebook notably switched from their version of BSD+Patents license to MIT (9/22/17)
  • There's React, React Native, React Native Web, React VR (soon AR?), and more.
  • Uses React: Magento "PWA Studio", Facebook, TYPO3, etc.

React? Notable Issue #'s

React? Opinion & Conjecture

  • React has been varying described as experimental, or destined for core...
  • "In my opinion, we still lack sufficient JavaScript expertise among Drupal core contributors." - Dries
  • React lib probably will ship in admin theme, not as a core lib
  • WordPress(.com) had halted their React efforts because of its prior license - presumably it'll go forward now?
  • I think Web Components are the future

So where are we now?

Alternatives to where we are now

  • Many are still pushing for Vue, the prior issue link is still quite active
  • Some are still pushing for web components (Polymer) but the standard's evolving nature are an issue

Thanks!

We are the Drupal Community - check out and consider joing the Drupal Association drupal.org/association/campaign/build-2017