by

Introducing DXAJS

Reading Time: 6 minutes

One of the really fun projects that we worked on over the 2017 SDL MVP retreat was one that involved one of my favorite things: JavaScript. In particular, using JavaScript with SDL’s DXA framework.

In two short days at the SDL MVP retreat, Bart Koopman, Raimond Kempees, Niclas Cedermalm, Saurabh Gangwar, and I, managed to get a very basic example of how to create a client-side, JavaScript framework that could run off of DXA. I’d like to explain a little bit more of what that is, and how it works

Why Client-side

It’s not uncommon these days that Tridion clients ask more of their CMS than for it to deliver generic content. Often, Tridion clients ask for interactivity. While interactivity can be supplied via traditional MVC + .net architecture, this often isn’t enough in the era of über fast page loads and native apps. This is where JavaScript frameworks such as Angular, React, Preact, and Vue come into place.

So It makes sense that DXA be able to work not only on the server, but the browser, as well. So Bart Koopman proposed that SDL have a client-side version of DXA, and, well, I couldn’t help it. I love JavaScript. So I happily obliged.

Show me the code

SDL is a firm believer in, “Sharing is Caring,”, so one of our first courses of business was to spin up a Github repo. We got to work on whipping up a model service, and then hammering out how in the heck we could do some templating.

The No-Framework Framework

The SDL MVP retreats are famous for doing insane amounts of work in short amounts of time. Considering the amount of alcohol, caffeine, and nicotine that runs in our veins, it’s honestly a miracle that our code runs as well as it does.

Being the front-end monkey on the team, I had a tough decision to make within the first five minutes: “Do I have time to pull down a Yeoman/Webpack package, configure it, and start getting to work? Or Should I just get straight to the code?

MVP retreats are famously short on time. We only have 2 days of work, and there’s lunch, dinner, and drinks in between. I didn’t want to lose precious hours pulling down a scaffolding setup and configure it. I decided to get straight to the code.

So, I opted to skip using a scaffolder or a bundler. Not only that, I made a choice to skip using an MV* framework entirely. It was all about POC, and I knew that I could pull something off in native JavaScript with minimal effort.

Using Native JS for Templating

One of the really cool things about React, Preact, and Vue is how they deal with templating out the views. In particular, they take advantage of two things: Shadow DOM, and Diffing.

That makes these so powerful is that they aren’t constantly updating the DOM. Instead, they’re using a new feature in the web browser called the “Shadow DOM“. In a nutshell, the Shadow DOM is a “pre-rendered” version of the DOM. This is important because, changes to the visible DOM are expensive and painful. Use jQuery to build any sort of labor intensive application and you’ll see: constantly reconstructing the DOM in front of the user sucks.

The aim of the Shadow DOM is to lend us the ability to construct and reconstruct the DOM without it being visible to the end user. So React, Preact, and Vue all use the Shadow DOM to render the DOM.

But they don’t just do that. They also have “diffing”. Which is to say, much like when you use a dif tool in Git that shows the differences between your last commit, these frameworks “Dif the DOM”; they check and see what’s changed—before deciding what to change. This means that when something changes in the DOM; it’s only whatever had to change.

So the Shadow DOM and the diffing are incredibly valuable features of these frameworks that make a huge difference in building web apps.

But we’re not trying to build web apps. We’re just trying to put a few pieces of content on a page. So I made an Executive Decision to skip the frameworks and go native.

How to go native in 1 easy step

Ok, turns out, there’s an easy way to skip all of the frameworks that give you these fun templating syntaxes: use template literals. I mentioned the value of template literals in a previous blog post, but now I can expand just a tad bit on what that looks like here:

DXAJS.Views = {
  Article(data) {
    const articleBodyContent = data.articleBody.content;
    const contentType = articleBodyContent.$type;
    let bodyResult = '';

    data.articleBody.content.Fragments.forEach((fragment) => {
      bodyResult += DXAJS.Partials[contentType](fragment);
    });


    return `
  <article class="article">
    <h1 class="article__headline"> ${data.headline}</h1>
    <div class="article__body body">${bodyResult}</div>
  </article>
    `;
  },
};

It’s pretty straightforward, as you can see. I send data into DXAJS.Views.Article(), and via the magic of interpolation, my content gets plugged in where it needs to go.

This works, and it was sufficient to get to a very basic POC, in about 2 hours. Of course, this has none of the features or value that could come with using a framework. But in a scenario where this content might not change for the lifecycle of a page, it works.

Regions

One of the more interesting parts of this is recognizing how the content model of the page actually works:

  • a page has Regions
  • Regions have Entities
  • Entities is an array containing component presentations
  • a component presentation has properties, which are objects, such as ComponentTemplate and Content

So if we want to be honest with our development, we need to start at regions, and work our way down.

So the easy way for me to accomplish that was to make an assumption that a page template would know, from the markup, what regions it had.

This mean that a page template denotes its regions in the markup, like so:

<main data-region="Main">

</main>

 

We use a data-* to indicate from the markup what the region is on a page.

From the JavaScript, then, we render regions in a few steps:

  1. Find out how many regions there are from the markup
  2. Find out how many regions there are in the page data
  3. Loop through the region elements
  4. Render all the entities for a given region element

So, DXA.Regions.renderRegions(pageData) ends up looking something like this:

renderRegions(pageData) {
  const regionElements = this.getRegionsFromMarkup();
  const regionData = this.getRegionData(pageData);

  regionElements.forEach((regionElement) => {
    const regionName = regionElement.dataset.region;
    const entities = this.getEntities(regionName, regionData);

    this.renderEntities(entities, regionElement);
  });
},

 

And renderEntities ends up being a loop that does the fun task of sending data into a view, and rendering it.

How does it all get put together?

After getting from zero to POC, the next step was to separate concerns and make some files that could be dedicated to a single job. And then get them working together again. So that means breaking out DXAJS into five files:

  • app.js
  • controllers.js
  • regions.js
  • routing.js
  • view.js

Then, Gulp is used to put the files back together in a single file that can be added to a web browser. I chose Gulp, rather than Grunt, because Gulp supports streams; the notion that I can do many manipulations in any particular order on a file, before it becomes a file. As I’ve written in the past, it can be a bit of a tedious task to do a lot of stuff to a file in Grunt. In this case, Gulp serves us well in case we want to add in minification or obfuscation for our final product at a later time.

So when might DXAJS be useful?

A question that comes up a lot in SDL circles, and in the front-end community in general, is, “What is the purpose of React/Angular/Vue/Preact/Ember/Handlebars?”

Their purpose is to deliver web apps.

I would argue that good old fashioned, every day, content delivery, doesn’t need a client-side framework. In fact, a client-side MV* setup may even be harmful to the user!

But, these frameworks do have value for making web apps. You don’t need React or Vue for a run of the mill brochure website. But what if that website needs to have UI controls that can fetch various pieces of content? What if a website needs faceted search? Or robust logins? Or interactive charts, or product selection tools? These are cases where it’s not just a simple case of rendering a piece of content, but instead, reacting to decisions the user is making in the browser.

Those kinds of heavy interactions will benefit from client-side frameworks that are built to work with the data that Tridion is providing. That is the time to have a JavaScript-ready framework for working with Tridion.

Will DXAJS use [flavor of the week]?

I hope not.

Again, DXAJS was just a prototype and a POC. It isn’t a viable product—yet. But I would like for it to, at some point, be a reliable NPM package that front-end or back-end developers could rely on for developing their DXA-based websites.

So to that end, I don’t want DXAJS to be tied to a particular framework. I think that it will work best when it provides solutions to Tridion-specific problems, like XPM, Routing, internationalization, session preview, or localization.

If DXAJS continues beyond a fun MVP Retreat POC, I think it will be best as a layer between DXA, and the flavor of the week that front-end developers are using.

What’s great is that Tahzoo (my employer), already implemented a client-side solution for DXA for one of their clients. I had nothing to do with that solution whatsoever— but it goes to show that it’s not only possible, but profitable, to deliver DXA + JS solutions. Tahzoo’s hope is that we can take lessons learned from that client, and this retreat, and apply them to a solution that the community at large could rely on.