Classic to Touch UI Migration for AEM: Multifields

Liubou Masiuk
Exadel Marketing Technology Practice
4 min readOct 18, 2019

In our previous post on Classic to TouchUI migration we focused on one quirk of this migration, Page Properties. In this post, we focus on one more quirk about what are called multifields.

Adobe Experience Manager provides a lot of different out-of-the-box fields for creating component dialogs. Multifield is one of them. Multifield is used for repeated instances of the same fields or groups of fields, allowing us to add, reorder, or remove its items. During the migration to TouchUI, we found out that this field is quite challenging in trying to ensure backward compatibility. Let’s look at the differences between ClassicUI and TouchUI multifields.

First of all, it depends on the complexity of a multifield item. Let’s assume we have a simple multifield where each item is a text field. In this case, the content will be stored as a multi-string value.

Simple multifield

But what if there are several types of field in a multifield item? Then it depends on whether we’re talking about a ClassicUI widget or a TouchUI component.

In the case of ClassicUI, the developers would usually have had to create a custom xtype in order to save several fields into one. The data was usually stored in JSON format.

Complex Classic UI multifield

For TouchUI, a CoralUI multifield component has out-of-the-box support of multifield items with several fields (i.e., composite multifield). But there is a catch. They are stored in subnodes under the component.

Complex Touch UI multifield

That causes a huge issue with backward compatibility and migration of components with such multifields to TouchUI.

Possible Solutions

So what can we do with this issue in TouchUI? We found several approaches proposed by AEM community members.

Option 1: Update the component’s code to retrieve data from nodes instead of JSON and create a script to convert all JSON data and save it into nodes, as suggested in this thread.

Pros:

  • We don’t have any problems with subsequent components created after migration
  • All data is stored in the same format compatible with the Coral3 multifield

Cons:

  • Changing large amounts of content via script in a production environment can be risky

Option 2: Switch to Coral2 multifield and use the ACS commons Multifield Extension.

Pros:

  • No changes to the content
  • No changes to the back-end multifield processing logic

Cons:

  • The ACS commons Multifield Extension is officially deprecated
  • The Multifield Extension does not support Coral3 multifield, so this is a short-term solution, which will require additional migration to Coral3 after Coral2 is deprecated

Option 3: Create a custom component that saves multifield items into JSON nodes, as suggested in this adaptTo() talk.

Pros:

  • No changes to the content
  • No changes to the back-end multifield processing logic

Cons:

  • In the long term this approach will require more maintenance and will never be fully compatible with OOTB Coral multifield

Our Solution

After weighing all the pros and cons of these solutions, we came to the conclusion that none of them are 100% suitable for us, so we came up with our own approach.

  1. The Coral3 multifield component is extended to provide backward compatibility with legacy JSON data
  2. New data is always saved into nodes
  3. When opening an old instance of component with legacy data, JSON is parsed into form fields
  4. When saving such a component, legacy data is replaced by nodes

This strategy gives us the following advantages:

  • No scripts and no risky changes to the content-the data will be gradually updated when the content is changed
  • New components are compatible with OOTB multifield, so at some point in the future we can potentially remove this customization and move back to Coral3 multifield

In order to implement this approach, first we need to create a new form component that extends Coral3 multifield. To achieve that, we need to point the component’s sling:resourceSuperType to

granite/ui/components/coral/foundation/form/multifield

Then, we modify the existing multifield so that it parses the legacy JSON structure and retrieves all existing fields. There is one specific edge case that we needed to cover. Some legacy components had only one field in the item, but still saved their structure into JSON. The resulting data looked like this:
{"url":"https://www.google.com"},{"url":"https://www.adobe.com"},{"url":"https://www.example.com"}

Such fields should be converted to simple TouchUI multifields. Here is what the component’s renderer.jsp file looks like (some parts omitted for brevity):

In order to retrieve multifield data in component model/wcmUse class, we also need to check whether the data is stored in nodes, a JSON array, or as a simple array. That’s why our MultifieldUtils class also contains a couple of methods that allow us to consistently work with stored data and get a list of valueMap objects independently of how data is currently stored in JCR.

Originally published at https://exadel.com on October 18, 2019.

--

--