An increasingly common use-case for enterprise web implementations is the need for components that support the ability to be displayed in context within a site, but also syndicated for consumption by third-party web applications. These third-party applications can be either internal or external (or often both) depending on the needs of the client.
Now obviously this is quite a broad topic and describing it in great depth could probably result in a small book. With that said, this article strives to touch on the high-level methodology of accommodating syndicated components when using Adobe Experience Manager as your content management platform. Additionally, it covers a few very common pitfalls when implementing syndication in real world scenarios.
At least an intermediate understanding of Adobe Experience Manager (AEM) along with common front-end web technologies is assumed.
Selectors & Template Structure
At a foundational level, AEM is based on Apache Sling, which helps us with regards to syndication before we’ve even started, that is, if we’ve designed our templates (and the page components which render them) correctly. This is because of a portion of the SlingHttpServletRequest called a ‘selector’, which represents the portion of the URI between the page name and the page extension:
Ex: /path/to/my/page.selector.html
This becomes extremely relevant when designing how your page components are structured, because each script within your page component is automatically addressable by means of a selector. For example, a very simple page component might consist of:
page.html
which includes:
head.html
which includes:
headlibs.html
metadata.html
body.html
which includes:
header.html
content.html
footer.html
footlibs.html
Now, realistically speaking a page component structure is generally a little more complex than this, but the above will serve to illustrate how useful this can be when syndicating components. So, using the example above, we can retrieve the contents of the header on a specific page by requesting:
/path/to/my/page.header.html
This is behavior that is built into Sling, and thus it works out-of-the-box with AEM.
/path/to/my/page.header.syndicated.html
Additionally, we can also add custom selectors that inform our scripts and Java objects to modify their behavior when present, so for example:
The above example would invoke the ‘header.html’ script (because that is the first selector) but would make the ‘syndicated’ selector available throughout the application stack in order to apply custom business logic that is necessary only when the header is being syndicated.
Absolute URLs vs. Relative
Probably the simplest example of how logic needs to differ between a syndicated component vs. that same component rendered in its native environment is a link. In a native environment, the links in a site header or footer can all be relative, assuming they are linking internally.
When that same component is syndicated, and especially when syndicated to an external environment, it becomes important that all links have absolute URLs, including protocol, host and uri.
Thus, you could in theory use the ‘syndicated’ selector from our example in the previous section to inform the header to render absolute URLs rather than (assume its default behavior) relative.
Client Libraries
Now, obviously we know that in order to render a functional header component, likely we don’t only need the HTML. Most likely there is CSS and Javascript needed in order for the component to look nice and function properly. The thing is, we probably don’t want to include all of the CSS and Javascript required for our native site – most likely it will be lighter on the consuming application if we only include the client-side scripts which are required to render the header.
So, assuming our CSS (and perhaps some early Javascript logic) is included in our ‘headlibs.html’ script, we could add logic to that script to only include relevant scripts if our ‘syndicated’ selector is present. We can take this one step further by passing in a third selector which informs the script which component we’re syndicating, for example:
/path/to/my/page.headlibs.syndicated.header.html
But what if we’re syndicating multiple components (say, the header and the footer)? We can define that any selectors that follow the ‘syndicated’ selector define a list of components for which we will include the client-side logic. So:
/path/to/my/page.headlibs.syndicated.header.footer.html
This same logic can be applied to the footlibs.html, which should contain Javascript that isn’t needed in the <head> and thus can be included separately at the bottom of the page.
3rd Party Client-Side Dependencies
One especially crucial thing to be aware of is the potential for collisions between different versions of 3rd party client-side dependencies. Take Jquery for instance; many sites use this library, and there are many different versions. We need to ensure that, if our components are dependent on Jquery we do something to mitigate any conflicts between our components and the consuming application.
There are many ways this can be accomplished, here are just a couple:
- We can namespace our version of the library so that it is stored in MyApp.$ (just an example) and use that object exclusively to address Jquery in our components. This will ensure that ‘$’ is not overwritten within the consuming application.
- We can exclude Jquery entirely and specify the library and the correct version as a dependency for the consuming app – this puts the onus on the consumer to ensure that they are including the appropriate version of the library prior to including our component(s).
There are likely more solutions for this issue, but you get the idea. The last thing we want is for a consumer to plug our component into their site and have things start breaking, so we need to be aware of all the ways this could potentially happen and do our best to solve in advance.
Conclusion
The ability to share components and their content between multiple applications is a powerful thing. It can go a long way towards optimizing the management of said components and enforce consistency across an organization’s web properties. However, it also comes with challenges that need to be addressed, otherwise the exact functionality your client is asking you to deliver to optimize their processes could, in fact, do the opposite.