AEM Experience Fragments: Consuming Outside AEM

Exadel Digital Experience Team Tech Insights July 30, 2020 7 min read

You can push an Experience Fragment (XF) to an endpoint by using, for example, the 3rd party’s API (e.g. Facebook / Pinterest). A 3rd party can also pull an XF from AEM. Every XF has a unique URL that can be embedded/used. Embedding an HTML XF can be achieved by using an <iframe> or with web components.

Currently, AEM supports HTML ootb (for Adobe Target HTML and JSON offers are supported) but you can implement any format (e.g. image, json, etc.). In addition, you can use the “plain” selector to get just HTML. You can configure the “Experience Fragments HTML rendering output processor” to render specific tags, attributes, and classes:

  1. Go to http://localhost:4502/system/console/configMgr
  2. Find “Experience Fragments HTML rendering output processor”

There’s another way to consume an XF on an external page. We can implement a servlet which grabs all the necessary libs (js, css, fonts, etc.) as well as as the XF content and puts them into your page.

Code example:

A simple servlet which scavenges XF content, the needed js, css and wraps them into a json object.

@Component(
       immediate = true,
       service = Servlet.class,
       property = {
               "service.description=Simple Servlet",
               "service.vendor=Site.com",
               SLING_SERVLET_RESOURCE_TYPES + "=site-com/page/xf",
               SLING_SERVLET_EXTENSIONS + "=js"
       }
)
@Designate(ocd = XFJSServlet.XFJSServletConfig.class)
public class XFJSServlet extends SlingSafeMethodsServlet {
 
   private static final String JQUERY_LIB = "/etc.clientlibs/clientlibs/granite/jquery.js";
 
   @ObjectClassDefinition(name = "XF JS Servlet")
   public @interface XFtJSServletConfig {
   }
 
   @Reference
   private RequestResponseFactory requestResponseFactory;
 
   @Reference
   private SlingRequestProcessor requestProcessor;
 
   @Reference
   private HtmlLibraryManager htmlLibraryManager;
 
   @Reference
   private Externalizer externalizer;
 
   @Reference
   private SlingSettingsService settingsService;
 
   private boolean isPublish = false;
 
   @Activate
   protected void activate(ExperienceFragmentJSServletConfig config) {
       if(settingsService.getRunModes().contains(Externalizer.PUBLISH)) {
           this.isPublish = true;
       }
   }
 
   @Override
   protected void doGet(@Nonnull SlingHttpServletRequest request, @Nonnull SlingHttpServletResponse response) throws ServletException, IOException {
       PrintWriter out = response.getWriter();
 
       response.setContentType("application/javascript");
       JsonObject xfJson = new JsonObject();
 
       xfJson.addProperty("reqJquery", getLink(request, JQUERY_LIB));
 
       // get page clientlibs
       xfJson.add("cssLibs", getMainLibraries(request, LibraryType.CSS, "pagelibs", "pagelibs-critical"));
       xfJson.add("jsLibs", getMainLibraries(request, LibraryType.JS, "pagelibs"));
 
       getPageClientLibsContent(request);
 
       // get html
       String contentHtml = getPageContentHtml(request);
       contentHtml = contentHtml.replace(""/content", String.format(""%1$s", getLink(request, "/content")));
       String formattedString = new String(Base64.getEncoder().encode(contentHtml.getBytes()));
       xfJson.addProperty("content", formattedString);
 
       out.print("var xfDeliveryObj = " + xfJson + ";");
       out.print(getXfDeliveryJs());
 
   }
 
   private JsonElement getMainLibraries(SlingHttpServletRequest request, LibraryType libraryType, String ... categories) {
       Collection<clientlibrary> libraries = htmlLibraryManager.getLibraries(categories, libraryType, true, true);
       JsonArray cssLibs = new JsonArray();
       for (ClientLibrary library : libraries) {
           cssLibs.add(new JsonPrimitive(getLink(request, library.getPath() + libraryType.extension)));
       }
 
       return cssLibs;
   }
 
   private void getPageClientLibsContent(SlingHttpServletRequest request) {
 
       Page page = request.getResource().adaptTo(Page.class);
       Designer designer = request.getResourceResolver().adaptTo(Designer.class);
       Design pageDesign = null;
 
       // traverse up to find design path
       while (page != null && (pageDesign = designer.getDesign(page)) == null) {
           page = page.getParent();
       }
 
       // default if not design path is set
       if (pageDesign == null) {
           pageDesign = designer.getDesign(Designer.DEFAULT_DESIGN_PATH);
       }
   }
 
   private String getPageContentHtml(SlingHttpServletRequest request) throws ServletException, IOException {
       Resource xfResource = request.getResource();
 
       String resourcePath = xfResource.getPath() + ".min.html";
       HttpServletRequest req = requestResponseFactory.createRequest("GET", resourcePath);
 
       WCMMode.DISABLED.toRequest(req);
 
       ByteArrayOutputStream reqOut = new ByteArrayOutputStream();
       HttpServletResponse resp = requestResponseFactory.createResponse(reqOut);
 
       requestProcessor.processRequest(req, resp, request.getResourceResolver());
 
       return reqOut.toString();
   }
 
   private String getXfDeliveryJs() {
       Collection<clientlibrary> libraries = htmlLibraryManager.getLibraries(new String[]{"site.xf-js"}, LibraryType.JS, true, true);
       StringWriter jsContentString = new StringWriter();
       for (ClientLibrary library : libraries) {
           HtmlLibrary htmlLib = htmlLibraryManager.getLibrary(LibraryType.JS, library.getPath());
           try {
               IOUtils.copy(htmlLib.getInputStream(), jsContentString, "UTF-8");
           } catch (IOException e) {
           }
       }
 
       return jsContentString.toString();
   }
 
   private String getLink(SlingHttpServletRequest request, String relativePath) {
      return isPublish() ? externalizer.publishLink(request.getResourceResolver(), relativePath) :
              externalizer.authorLink(request.getResourceResolver(), relativePath);
   }
 
   public boolean isPublish() {
       return isPublish;
   }
}
</clientlibrary></clientlibrary>
See more See less

A custom js lib which consumes json provided by the servlet and renders it:

.content.xml:

<!--?xml version="1.0" encoding="UTF-8"?-->
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" jcr:primarytype="cq:ClientLibraryFolder" categories="[site.xf-js]"></jcr:root>
 
Index.js:
(function(){
   console.log(xfDeliveryObj);
   var jqueryLib = xfDeliveryObj.reqJquery;
 
   var current = document.currentScript.parentNode;
 
   if (typeof jQuery === 'undefined') {
       current.appendChild(createScript(jqueryLib));
   }
 
   xfDeliveryObj.cssLibs.forEach(function(lib){
       current.appendChild(createStyle(lib));
   });
 
   xfDeliveryObj.jsLibs.forEach(function(lib){
       current.appendChild(createScript(lib));
   });
 
   current.insertAdjacentHTML("beforeEnd", b64DecodeUnicode(xfDeliveryObj.content));
 
   function createScript(link) {
       var el = document.createElement("script");
       el.src = link;
       return el;
   }
 
   function createStyle(link) {
       var el = document.createElement("link");
       el.rel = 'stylesheet';
       el.href = link;
       return el;
   }
   function b64DecodeUnicode(str) {
       return decodeURIComponent(Array.prototype.map.call(atob(str), function(c) {
           return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
       }).join(''))
   }
})();
See more See less

After that we can easily put an XF in an external page:

 <title>xf_test</title>
 
 
<div>
   <div>External Content</div>
       <script src="https://www.site.com/content/experience-fragments/site-com/xf/xf.js"></script>
</div>
See more See less

Author: Iryna Ason

Was this article useful for you?

Get in the know with our publications, including the latest expert blogs