May 3, 2020
Estimated Post Reading Time ~

Front-End Adventures in AEM: Part II

In this I want to talk about what makes up our clientlib’s composition including leveraging built-in AEM dependencies, utilizing SPA (Single Page Application) frameworks for components, and a brief rundown of the new SPA Editor feature introduced in AEM 6.4.

As a reminder, this article series is broken into several parts as seen below. Feel free to jump around to the parts that seem interesting to you and please feel free to ask any questions you have about the material covered.

*note* — This article series is written with AEM 6.4 in mind but the concepts should apply to future versions and even some older (6.2) Touch UI versions.

*note* — Adobe is notorious for moving their documentation around and their helpx docs and forums often have broken links. I’ll occasionally link to their docs so if the links stop working let me know and I’ll try and update the article.

Leveraging Built-in Dependencies
AEM ships with several clientlibs out of the box including popular JS libraries like: Backbone, jQuery, jQuery UI, Moment and Lodash. However, outside of maybe jQuery, I wouldn’t include these built-ins for any of my client facing clientlibs. Instead, if you need any of these libraries, consider adding custom versions built around your component’s needs.

For author facing clientlibs, most of these (and others) are available on the window object in the editor. Just make sure your author facing clientlib has dependencies set to these libraries that you need to ensure the proper load order.

To jQuery, or not jQuery; That is the Question
Speaking of jQuery, let’s address the elephant in the room. You might not need jQuery though it might be a good fit for your AEM project. There are several things to consider if you’re planning on excluding jQuery from your clientlibs:
  1. jQuery is still a requirement for the editor. This means jQuery will be loaded in the outside editor frame when content authoring. This does not mean jQuery must be used for your client facing pages (where it’d be an actual performance concern) but it’s useful to keep in mind nonetheless. There is no escaping jQuery entirely within the confines of AEM.
  2. jQuery is still a requirement for the ContextHub. So if you’re using ContextHub, you’re using jQuery.
  3. You may want to delegate your events. Whenever a component’s dialog is updated in the AEM editor, AEM removes the component’s markup from the DOM, updates, then re-injects the component’s HTML back into the DOM. This wreaks havoc on any set event listeners that were attached to the old DOM nodes.This can be circumvented with Event Delegation which jQuery has baked in via .on(). There are other libraries that can do this too like Gator or delegated-events.
  4. You might need Promises. IE11 is still a common support target in todays web and it does not support Promises. You can polyfill, or you can defer (pun intended) to jQuery’s Deferred.
  5. You might want an AJAX library. Fetch users need not apply, but if you’re using AJAX you might want a helper library. jQuery has an excellent AJAX library built in.
  6. If making AJAX/Fetch requests to AEM, you get CSRF out of the box with granite.jquery. However, you can still use the framework separately using granite.csrf.standalone.
  7. Core components *may* require jQuery. It’s a fairly common practice to leverage AEM’s core components and these component’s might rely on jQuery in various settings. Fortunately, you don’t have to just assume, you can check out the component’s source on Github or you can always look in your own JCR.
To use a core component as an example, the Accordion component’s site clientlib does not require jQuery but it’s editor clientlib does. If all you’re using is Accordion’s from Core, you wouldn’t need jQuery for client-facing pages.

Should I use the built-in jQuery or provide my own?
The built-in jQuery, as of this writing, is a modified version on the 1.x branch of jQuery. This means it’s going to have higher browser support and a larger payload size. I’d probably look into a custom build to save bytes.

In summation… it depends!
Whether or not you choose to use jQuery should be dependent on the project’s needs, not a particular developer dogma. It’s certainly not a requirement but there are many features that might make it a good fit.

What is a “SPA” Component?
SPA Frameworks are JavaScript frameworks/libraries and technologies that enable building sites and applications on a single HTML page by dynamically rewriting the current page’s DOM rather than loading entirely new pages from a server. As of this writing, the three major players are React, Angular, and Vue.js but this is far from all of them. To get an idea of just how many there are out there, I occasionally visit Stefan Krause’s JS Framework Benchmark Github project which contrasts various framework performance metrics.

To that end, a “SPA” Component is a component dependent on a particular SPA/JS Framework. To help illustrate, let’s show off some example markup of a traditional component and a SPA component:
“Traditional” AEM Component

<sly data-sly-use.accordion="com.mysite.aem.components.Accordion"/>
<a data-sly-test="${accordion.anchor}" name="${accordion.anchor}"></a>
<details class="accordion" open="${accordion.openByDefault}">
  <summary class="accordion__summary">${accordion.title}</summary>
  <div data-sly-resource="${'parsys' @ resourceType='wcm/foundation/components/responsivegrid'}"></div>
</details>

You can see that the “traditional” component above contains all of the elements needed to make up the component.
“SPA” AEM Component (i.e. for a framework like React)

<div
  data-sly-use.search="com.mysite.aem.components.Search"
  id="search"
  data-search-title="${search.searchTitle @ context='unsafe'}"
  data-api="${search.api @ context='unsafe'}"
  data-no-result-text="${search.noResultText @ context='unsafe'}"
  data-result-count-text="${search.resultCountText @ context='unsafe'}"
  data-pagination-first-label="${search.labelPaginationFirst @ context='unsafe'}"
  data-pagination-last-label="${search.labelPaginationLast @ context='unsafe'}">
  <script>dynamicRender("#search");</script>
</div>

In contrast, a “SPA” component could simply be a <div> with a bunch of data-* attributes to feed component dialog data to the JavaScript framework. This isn’t always the case and would depend highly on the JS framework chosen.

In the above example, I’ve added a <script> which calls a window method to render this component. It seems obtuse at first but this allows the component to work within AEM’s editor without a hitch. Any time the component is updated, the dynamicRender method is re-run and the JS Framework replaces the <div>’s DOM removing any trace of the <script>. Easy, if maybe a little inelegant.

As far as AEM is concerned, there are basically two kinds of JS Frameworks:
  1. Compiled — A framework where your markup is “compiled” into JavaScript or is written in JavaScript from the start. These typically require some sort of build step to run. This is the most prevalent type today and includes many frameworks like: React, Angular, Vue.js, and Svelte.
  2. Interpreted or “Decorator” — Instead of compiling your markup, your HTML is decorated with attributes/directives that make it possible for JavaScript to intelligently update the elements it needs to. This includes frameworks like: AngularJS, Vue.js, and Aurelia.
Vue.js is really flexible and offers both compiled and decorator options. However, the former is more performant and is probably what you should be using in production.

AEM can work with either type but how you handle it will be slightly different. Depending on how you set up your framework, AEM’s editor might “just work” with a decorator framework whereas you’ll have to find some workarounds (like I did above) for a compiled framework.

Choosing a SPA Framework
When choosing a JS framework for AEM, there are a few things to consider:
  1. Should you use a SPA/JS Framework at all? JS Framework-based components can act a little wonky in AEM’s editor and there isn’t an easy way to do Server Side Rendering on AEM so SEO will be a concern for components leveraging the framework. JS frameworks should be relegated to components that require heavy DOM manipulation and/or API driven markup. Search Engines, Product Filters, Wizards, etc. are good examples of components that I think make sense as “standalone SPA components”. Do not use JS Frameworks for Accordions, Buttons, etc. even if you’re using the JS Framework elsewhere in AEM.
  2. How will your content authors update this component? As previously mentioned, when updating a component’s dialog AEM will remove that component from the DOM and then re-insert the updated markup. A big sell to AEM is it’s authoring features so be sure that your SPA components work well within the authoring experience.
  3. Are you using the SPA Editor? If so, your choices as of this writing are either React or Angular.
  4. Are you getting more than you need? Focus on the features your components are going to need. For example, within the confines of AEM, you’re not going to need routing as that’s handled via Sling. I tend to prefer an à la carte approach verses an everything but the kitchen sink approach.
Anecdotally, I see a lot of support to React with AEM. It works with the SPA Editor features, is performant, has a strong community, works well within the editor (with some considerations), and is solely concerned with manipulating the DOM so it doesn’t include routing, state management, etc. out of the box.

However, always choose the tool that makes sense for your team and project!

Introducing the AEM SPA Editor
Introduced in AEM 6.4 Service Pack 2, the SPA Editor feature lets you build a full single page application on top of AEM while retaining all of the editors features your content authors have come to know and love. In order to use the SPA Editor, you must be using Sling Models that can export JSON. Also, React and Angular is currently the only supported frameworks and is used to render the entire page, not just select components as outlined above with “SPA” Components.

The SPA Editor Overview does a great job of explaining how all of this works under the covers in detail but here’s a rundown from the Overview:
  1. SPA Editor loads (in the parent authoring frame).
  2. SPA is loaded in a separate (the content) frame.
  3. SPA requests JSON content and renders components client-side.
  4. SPA Editor detects rendered components and generates overlays.
  5. Author clicks overlay, displaying the component’s edit toolbar.
  6. SPA Editor persists edits with a POST request to the server.
  7. SPA Editor requests updated JSON to the SPA Editor, which is sent to the SPA with a DOM Event.
  8. SPA re-renders the concerned component, updating its DOM.
If you’re looking to try out the new editor, I highly suggest going through the WKND Events tutorial. It outlines the various NPM packages you’ll need as well as how everything interacts.

Op-ed on the SPA Editor
I haven’t had a chance to play with the SPA Editor on a real project yet and even the stuff I did play with was a bit wonky. The NPM packages were poorly documented so it was difficult to get a good feel for all you could do outside of what was outlined in the WKND Events tutorial. Overall, I’m not sure I’d recommend it for anything in production outside of maybe internal facing applications where SEO isn’t a concern. Still, I think the future is really bright for the SPA Editor if Adobe can fix a few things.

I’d love to be able to use SPA Editor features for standalone SPA components instead of entire pages. It’d be great to have a common “button” component that I could use not only standalone but also within my SPA-based search engine component. While I can do that visually for my end users today, it’s not nearly as author-friendly as a SPA Editor approach would be.

I also want better documentation on why we need 3 separate NPM packages to make the SPA Editor work as well as MUCH BETTER documentation on the individual packages themselves. I understand separate packages for React and Angular but why do I need @adobe/cq-react-editable-components, @adobe/cq-spa-page-model-manager, and @adobe/cq-spa-component-mapping to make my React SPA work? Can’t we just get one package for each framework? Tree-shaking is a thing. Even if it wasn’t, it doesn’t seem like the packages are split across editor/content lines.

Again, I’d probably skip this for now but it’s something to pay attention to moving forward!

Conclusion
Managing your dependencies in AEM should require some forethought. It’s important to evaluate what tools are needed for your project, not just what’s been used in the past or what’s the latest craze. Now that we have a deeper understanding of what belongs in our clientlibs, let’s look at our markup!

WRITTEN BY: Benjamin Solum


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.