Using Zen Garden to populate AEM Touch UI objects

15 July 2016
Piotr Wilczynski
Frink_Cognifide_2016_HeaderImages_0117

Overview

Building a dialog for an AEM component is not always easy and the dialog is not always static. Sometimes there is a requirement to provide dynamic value for it. AEM comes with a useful mechanism which is used in Granit UI components. DataSource is a factory to provide a collection of Resource items. It is useful to provide dynamic items for Touch UI components.  However, I'd also like to show you an alternative method using Cognifide's Zen Garden.

Using DataSource

In this article Adobe describes how to use DataSource in a jsp file.  However, instead of jsp, we can use servlet. Let's take a look at an example providing values for an HTML select field. To obtain available themes for the page we can use the following example:

<theme    
	jcr:primaryType="nt:unstructured"    
	sling:resourceType="granite/ui/components/foundation/form/select"    
	name="./theme"    
	fieldLabel="Theme">    
	<datasource        
		jcr:primaryType="nt:unstructured"        
		sling:resourceType="page/availableThemes"/>
</theme>

Our field contains a datasource tag which will populate select using page/availableThemes resource, which is handled by Sling servlet:

@SlingServlet(resourceTypes = "page/availableThemes")
public class AvailableThemesServlet extends SlingAllMethodsServlet {
	@Override
	protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
			throws ServletException, IOException {
		List<Resource> themes = null;
		...
		DataSource dataSource = new SimpleDataSource(themes.iterator());
		request.setAttribute(DataSource.class.getName(), dataSource);
	}
}

The Zen Garden way

The DataSource approach is not sufficient, because you have to write a new servlet for each select. In Zen Garden we want it to be backward compatible with ExtJs components. These components use servlets which produce output in json format. In order to avoid the need for too much developer generated code, we have decided to create a DataSourceTrasformer service, which is responsible for converting any kind of data into DataSource objects. The same service reference properties can be used both in Classic and Touch UI. To make it even more flexible we have created a custom datasource resource (zg/commons/components/datasource) with extra parameters to reach proper data:

  • extension 
  • path
  • provider
  • queryString
  • selector

These parameters can modify a DataSource request which give more control over the component.

<theme
    jcr:primaryType="nt:unstructured"
	sling:resourceType="granite/ui/components/foundation/form/select"
	name="./theme">
	<datasource jcr:primaryType="nt:unstructured"
		sling:resourceType="zg/commons/components/datasource"
		selector="availableThemes"/>
</theme>

We have created a default implementation of transformer to convert components written in Classic UI, which is used if no provider is set in the datasource. We have also made it possible to write custom implementations of transformer, to populate data in different way. These implementations will transform data returned by DataSource request: 

@Component(immediate = true)
@Service
public class CustomDataSourceTransformer implements DataSourceTransformer {
	@Override
	public String getName() {
		return "custom";
	}

	@Override
	public DataSource provide(String data, SlingHttpServletRequest request) {
		ValueMap parameters = request.getResource().getChild(DataSourceTag.CONFIG_NODE).getValueMap();
		return makaDataSource(data, parameters);
	}

	private DataSource makeDataSource(String data, ValueMap parameters) {
		...
	}
}

Data passed as a string has to be converted and returned as DataSource object. To use this transformer you just have to pass the name as a parameter in the datasource tag in provider property:

<theme
    jcr:primaryType="nt:unstructured"
	sling:resourceType="granite/ui/components/foundation/form/select"
	name="./theme">
	<datasource jcr:primaryType="nt:unstructured"
		sling:resourceType="zg/commons/components/datasource"
		selector="availableThemes"
		provider="custom"/>
</theme>

Which is equivalent to the Classic UI field before conversion: 

<theme
	jcr:primaryType="cq:Widget"
	name="./theme"
	options="$PATH.availableThemes.json"
	type="select"
	xtype="selection"/>

Note the availableThemes selector used in both snippets. The conjunction of path (default is current page) and extension (default is JSON) will create URI used on the server side to call for original options and then pass the response to the DataSourceTransformer as data parameter.

These changes make the conversion of a Classic UI dialog to a Touch UI much simpler and flexible. We were able to use the same mechanisms as in Classic UI without writing a custom DataSource factory for each dialog or modifying servlets to handle a specific resource object. You can check out our previous blog Multifield component in AEM TouchUI dialogs: the Zen Garden approach to learn how to use multifield in Zen Garden 4.1 Fluorite.