November Happy Hour will be moved to Thursday December 5th.

John-Philip Johansson
Dec 19, 2017
  5310
(4 votes)

Taking more control of client-side rendering in OPE (Beta) (CMS UI 11.2.0)

This feature has changed in CMS UI 11.4.0! The event is no longer needed. Read the blog post for more info.

You can read more about how you enable Beta features in the documentation.

Earlier, in CMS UI 10.12.0, we introduced some features that let you take some control over the client-side rendering in On-Page Edit (OPE). One of those tools was the "beta/contentSaved" topic that you could subscribe to to know when to update the DOM. With CMS UI 11.2.0, we address the other side of that coin, which is letting us subscribe to know when you updated the DOM and that we need to remap the OPE overlays.

Here we’ll go through one new topic that you can use to further improve the OPE experience while using Angular, React, or any other JavaScript framework. You can read more in the Editing documentation.

The global JavaScript object (see "Telling OPE that you need new overlays"):

  • epi.publish("beta/domUpdated")

Knowing when to remap the OPE overlays

One common scenario is when you have an element with the property "data-epi-property-name" that will enable the OPE overlay, but since this element is not present when the OPE overlays are created, you don't see an OPE overlay for the element. This makes editing that property slightly challenging in OPE and once again you have to direct your editors to the All properties view. The Sticky View Mode can keep your editors from switching views all the time, but it would be nicer if OPE just works, right?

There are several situations where the OPE overlay doesn't match the DOM:

  • Element is conditionally rendered by the client-side framework
    • Example: A text that's only shown when the user reaches the last page in a pagination
  • Element is replaced by another similar element but using another "data-epi-property-name" value
    • Example: When loading the value was "fact1", and after user interaction the value is "fact2"
  • Element is rendered after the page has loaded
    • Example: Data is fetched through an API and only renders once it's done

We'll focus on the first two examples because they're easier to demo. For the user, it can look like this:

Image before.gif

Telling OPE that you need new overlays

We felt that using a message to communicate, like with "beta/contentSaved", was a nice solution so we decided to create a new topic: "beta/domUpdated". To tell the OPE view to remap all the overlays on the page, simply publish to that topic using the global "epi" object:

epi.publish("beta/domUpdated");

Note: You cannot subscribe to this topic from the view because the communication only goes from and to the iframe where the page view lives. A gadget or other code on the CMS UI side would be able to subscribe though.

Considerations when using an over-powered feature

With the new topic, it's possible to accidentally spam messages, prefering to publish one too many than one too few. This causes a few problems.

  1. UI becomes unresponsive as the spamming is ongoing
  2. Memory usage increases

To solve problem 1, we added a debounce period of 100 milliseconds where each new message resets the cool down period. This causes a small (100ms) lag before the OPE overlays are remapped.

To solve problem 2, we had to do several performance improvements, that also improve memory usage in other scenarios, such as changing pages (context change). All in all, we've made dozen small fixes, but as we say in Sweden: "Many small streams make a big river."

In one scenario we tried, we made a Higher-Order Component in React that would publish a message on the "beta/domUpdated" topic on componentDidMount and componentDidUpdate. The problem was that we created a new wrapped component in the render method, and that lead to an exponential growth of messages every time something rendered. The lesson is that it's very easy to accidentally publish too many messages. :) As the great American poet Ben Parker once said: "With great power, there must also come great responsibility". So try to consolidate your DOM changes to as few messages as you can.

A simple example in React

The message in "domUpdated" is simpler than the "contentSaved" because there is no message object involved. To demonstrate this we're just going to publish the message in our ThreeKeyFacts' componentDidUpdate method, but you should try to consolidate the messages so each component doesn't live its own life and spam "domUpdated" messages.

import React from "react";

export default class ThreeKeyFacts extends React.Component {

    constructor(props) { ... }

    render() { ... }

    next() { ... }

    previous() { ... }

    componentDidMount() {
        // Here we do some epi.subscribe("beta/contentSaved", ...)
    }

    componentDidUpdate() {
        if (window.epi && window.epi.publish) {
            // Publishing a message on the "beta/domUpdated" topic will tell episerver UI that the DOM has changed 
            // and that it needs to remap its overlays.
            window.epi.publish("beta/domUpdated");
        }
    }
}

By publishing a message on the "beta/domUpdated" topic in componentDidUpdate the overlays now match the elements being shown:

Image after.gif

 

Code on Github

Please contribute to these repos to help each other with how to work with OPE and your favourite JavaScript framework!

The example above is in our React sample repo: https://github.com/episerver/AlloyReact

Please contribute an example to the AngularJS repo: https://github.com/episerver/AlloyAngularJS

Dec 19, 2017

Comments

John-Philip Johansson
John-Philip Johansson Feb 13, 2018 10:43 AM

If you rather not do any framework dependent code and just have a global mutation observer that publishes the "beta/domUpdated" event on any change to elements with the "epi-property-name" attribute then Ben McKernan has a nice gist for you: https://gist.github.com/ben-mckernan/774ae408d19babd4d5c7547b4c9aa8a4

Please login to comment.
Latest blogs
Optimizely SaaS CMS + Coveo Search Page

Short on time but need a listing feature with filters, pagination, and sorting? Create a fully functional Coveo-powered search page driven by data...

Damian Smutek | Nov 21, 2024 | Syndicated blog

Optimizely SaaS CMS DAM Picker (Interim)

Simplify your Optimizely SaaS CMS workflow with the Interim DAM Picker Chrome extension. Seamlessly integrate your DAM system, streamlining asset...

Andy Blyth | Nov 21, 2024 | Syndicated blog

Optimizely CMS Roadmap

Explore Optimizely CMS's latest roadmap, packed with developer-focused updates. From SaaS speed to Visual Builder enhancements, developer tooling...

Andy Blyth | Nov 21, 2024 | Syndicated blog

Set Default Culture in Optimizely CMS 12

Take control over culture-specific operations like date and time formatting.

Tomas Hensrud Gulla | Nov 15, 2024 | Syndicated blog

I'm running Optimizely CMS on .NET 9!

It works 🎉

Tomas Hensrud Gulla | Nov 12, 2024 | Syndicated blog

Recraft's image generation with AI-Assistant for Optimizely

Recraft V3 model is outperforming all other models in the image generation space and we are happy to share: Recraft's new model is now available fo...

Luc Gosso (MVP) | Nov 8, 2024 | Syndicated blog