The JS frameworks have changed quite a lot in Drupal especially with API-first concept adding to the scenario. It is only expected that developers are inclined towards learning more about JS and related possibilities.
Recently, I was tasked to render blocks on a page while keeping individual components encapsulated in their own. JavaScript has the potential to make web pages dynamic and interactive without hindering the page speed and so I decided to opt for progressively decoupled blocks.
I came across this Drupal module Progressive Decoupled Blocks, which allows us to render blocks in a decoupled way and seemed a perfect fit for the situation.
The module is javascript-framework-agnostic, progressive decoupling tool to allow custom blocks to be written in the javascript framework of one’s choice. What makes it unique is one can do all this without needing to know any Drupal API.
It keeps individual components compact in their own directories containing all the CSS, JS, and template assets necessary for them to work, and using an info.yml file to declare these components and their framework dependencies to Drupal.
I decided to test the module in the new Umami Demo profile, that comes out of the box with Drupal, with React and Angular blocks.
I used the Layout module and picked a page to place these blocks. This is one of the best parts I liked about the module, every piece of block content that we need to be placed on the page, can be done without even visiting the block page and setting visibility.
The module has a custom block derivative, which searches for all components that have been added and exposes them as blocks. This architecture makes it super easy to add a new block.
You can refer to this git for the block derivative - https://git.drupalcode.org/project/pdb/blob/8.x-1.x/src/Plugin/Derivative/PdbBlockDeriver.php
Refer to this git for component discovery - https://git.drupalcode.org/project/pdb/blob/8.x-1.x/src/ComponentDiscovery.php
For React JS there are 2 examples in this module.
First is with a simple React Create element class, without JSX. This example renders a simple text inside a React component. This is a very good starter example for anyone who wants to understand how to integrate a react component with Drupal in a progressive way.
The second example is a ToDo app, which allows you to add and remove todo items to a list. It makes use of local storage to store any item that has been added. This app creates React components, integrate these components in other component and renders a fully functional DOM inside Drupal DOM in a progressive way.
This example comes with a package.json which needs to be installed before making it functional. However, the component did not render perfectly on Umami theme, I made certain changes to make sure it renders correctly. Patch attached in the end.
I decided to extend this module and added a new component to render a banner block (comes OOB with Umami), and exposed this block as an API via JSON:API. As this was a very simple block, I decided to create this block without JSX. Also, I decided to generate the URL with static ID in the path. This can, however, be made dynamic, which I plan to do later in my implementation. I also decided to choose the same class names to save on styling. The classes can be found in the Umami theme profile.
(function ($, Drupal, drupalSettings) {
Drupal.behaviors.pddemo = {
attach: function (context, settings) {
$.ajax(drupalSettings.pdb.reactbanner.url, {
type: 'get',
success: function (result) {
var image = result.included[0].attributes.uri.url;
var title = result.data.attributes.field_title;
ReactDOM.render(React.createElement(
'div',
{
className: 'block-type-banner-block',
style: {backgroundImage: 'url(' + image + ')'}
},
React.createElement(
'div',
{className: 'block-inner'},
React.createElement(
'div',
{className: 'summary'},
React.createElement(
'div',
{className: 'field--name-field-title'},
title
)
)
)
), document.getElementById('reactbanner'));
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
console.log(textStatus || errorThrown);
},
});
}
};
})(jQuery, Drupal, drupalSettings);
Angular JS
Angular JS comes with many more easy and complex examples. Things didn’t work as smoothly as it did with React. There were a couple of changes we were required to make it work.
Please refer to this gist for the patch.
You will have to install node modules before, to make it work. Also, all JS is in the form of Typescript, which needs to be processed to JS before you can make it work.
The module gives you a great kickstart to move any block, rendered by Drupal, to a progressively decoupled block, using React, Angular or Ember as JS framework. However, you may want to extend the module or create your own to render the blocks.