Showing posts with label Liferay. Show all posts
Showing posts with label Liferay. Show all posts

2021-10-31

HTML5 SLOT tag for content management explained

Web Components stack includes efficient and well thought through templating solution with TEMPLATE and SLOT tags. While those have been widely adopted in web components world for multiple reasons, the shadow DOM behind of this tech prevents to use it in environments where UI styles managed separately and often driven by heavy legacy content: while styling in shadow DOM has own perks like css insulation, it does not work well with global css styling. 

I would suggest to utilize the concept of slot in template  in content management environments with classic css use. You could implement the convention by yourself with showing/hiding the slot with name  


el.querySelector('slot[name="final"]').hidden = false;

To avoid the UI flickering , make sure the initial HTML includes the visibility state meant to be shown before JS is able to act:


<slot name="initial">Please wait while loading...</slot>
<slot name="final" hidden> initially is hidden, filled by JS and then shown </slot>

 

NOTE. The hidden attribute behavior is self-explanatory and works out of the box, no need for CSS. But if your environment is a CMS like Adobe AEM, Liferay, or Drupal, the CSS on the page is most likely a product of multiple people/teams over the years. Here the default behavior could be tricked by different CSS rules and you would need to elevate:
        
slot[hidden]{ display:none!important;}

Of course the page level javascript and HTML would limit only one slot name a time. Wrapping slots combinations into identifiable by JS container would help to handle multiple UI components with same kind of UI pattern(template). Like the avatar in the list and in profile UI. 

The most convenient way to connect JS and HTML element is a custom element, i.e. Web Component with own tag. `this` in its methods would point to html element of own tag which would be triggered every time the tag appear in HTM, initially or dynamically later. In CMS it would prevent the messing with page, document, and script load handlers, a huge win for code maintenance when the code is spread across developers/teams and time.

Web Component does not dictate the use of ShadowDOM and allows to pick any way of template implementation. There are multiple "frameworks" with opinionated ways of templates handling. The HTML content meant to be handled by middle tier in classic CMS. Where the templates also play important role in content lifecycle. Adding another templating engine is not just increasing the complexity but also created the conflict between different approaches and handling the content variations like multi-lingual support or cohort targeting.  Hence ability to reuse the CMS HTML capabilities along with template/slot concept would be most beneficial.

As CMS usually is not designed to serve the dynamic data, the fusion of externally served data and UI from CMS becomes a data plumbing task on UI tier. During this "data plumbing" process the UI should support multiple states including initial waiting, loading, error of multiple kind, and final with rendered data, perhaps with variations on data internals.

slotted-element comes handy for eliminating the coding for data plumbing in simplest cases and simplifying the programming otherwise. 

Here this custom element is a sample for typical data plumbing to CMS driven HTML tasks.

  • switch between slots to be shown. Hide all, display only one. 
  • clone slot content to be filled with data
  • add customized slot. Could be more than once for list.
  • data retrieval layer: url/method/headers/body, status, error handling, fetched data conversion.
  • UI sub-elements access and manipulation convenience methods.

Whether you choose to use slotted-element, extend it, or decide to create own, keep in mind the list ^^. 

Links:

Using templates and slots



2019-06-04

Liferay polymer integration

This is a guideline for Polymer 3 PWA integration as microapplication into Liferay Portlet. The npm module code is a part of portlet and deployed within its JAR. Sample has been build with Vaadin template.

Methods described in this article is applicable to any framework from Angular to Ionic. I am focusing on Polymer 3 as most compatible and smallest base. Other frameworks need extra level of compatibility coding in order to be Liferay (or any CMS) friendly.

Problem

Modern JS frameworks in Liferay 7.1 CMS are the pain to deal with.

The internal CDN publishing of NPM modules have promised to be an great helper. The build tool uses the package.json and node_modules content to publish module and dependencies version into Liferay URL. This way same package version could be shared among applications.

This brilliant idea had not been implemented sufficiently to be suitable for high-level frameworks like PolymerJS(native web components) or Quasar( VueJs ). Such libraries use dependencies in es6 source format which meant to be compiled and bundled depend of target platform into es5 or es6 bundles. Liferay JS toolkit unfortunately treats all dependencies as pre-build to es5 AMD format. Kicking off most of modern JS libs which use ES6 'import' capabilities. Bug.

Solution

Is to use selected platform bundled build capabilities. I would suggest to use legacy (es5/AMD) and modern(ES6) browsers build profiles, both as bundles with dynamic code load. This way in majority of browsers only minimal set of  JS will be used during page load still keeping page responsive to user actions when on-demand code is loaded.

The reference implementation also gives extra perks like

  • debug mode support to use unbundled JS sources on page load
  • build profile enforcing via URL parameter
  • JS project integrated and deployed withing portlet JAR.

Design notes

  • Portlet have to be published in fixed osgi path.
    I.e. for CSS /o/my-custom-portlet/my-custom.css deployment path defined by bnd.bnd is:
        Web-ContextPath: /my-custom-portlet
  • Legacy and es6 builds have to set own root path in index.html by build.sh
  • Browser compatibility recognition done on server side by BrowserSnifferUtil and uses the build profile matching browser.
    esm-bundled, es5-bundled profiles are made for modern and legacy browsers respectively.
  • Server-side inclusion of build/${profile}/index.html is done by jsp:include
  • the top-level component included by view.jsp after as tag with data passed as html element parameters. In sample the URL for ajax call is passed from jsp to portlet.
  • <script type="module"... > during navigation into page with PolymerPortlet is ignored by Liferay SPA implementation. view.jsp has a workaround by re-injecting script tag into page. Processed script tags are marked by `injected` property.
  • es5-unbundled generates document.write("<!--") which clears the page. Fix is removing code from generated index.html

Sample Code


Happy coding!