April 11, 2020
Estimated Post Reading Time ~

How to fetch Properties from Dialogs

One of CQ’s strongest assets is its ability to create and reuse custom components. A component’s reusability depends on how flexible and configurable it can be made. CQ is unlike other component-based frameworks like Adobe Flex because properties or options cannot be passed in, say, places like the cq:include tag. Instead, CQ properties and options are configured on pages via dialog boxes. Here, we will discuss how to retrieve component properties set by these dialog boxes.

If you’re just looking for quick code examples, here are snippets to fetch properties from within a component containing them, and from outside a component.

Step 1: Create a component. 

My example will be called “myComponent”

Step 2: Create a dialog node with some properties. 
For convenience, I’ll just be copying mine from an arbitrary sample component packaged with AEM.
Copy the node /apps/geometrixx/components/lead/dialog Into your myComponent folder. Notice it comes with two items, title and text. Notice the following properties of the “title” node:
A more detailed description of how dialogs work is outside the scope of this text, but for now, we’ll focus on the “name” property and the “xtype” property. Widget xtypes define, in part, how a property is stored. The xtypes of textfield and textarea, which are used in our example, happen to both store their data as java String types. the name property defines the variable name under which the property is stored. In this case “jcr:title”.

Step 3: Create an instance of the component within a page.

This can be accomplished via drag + drop, or hard-coding into another component (like a page component) with an include tag:
<cq:include path="myComponent" resourceType="matelli/components/content/myComponent" />

Tip: component properties cannot be set dynamically the way other platforms, like mxml, allow.

AEM components do not work like the image above. The title property must be modified by visiting the page displaying this component and adding text within the dialog box. (Note: exceptions outstanding, but not covered in this text).

Step 4: Add some text to the title property.

Be sure you’re logged into an author environment with a permissions user (e.g. admin/admin)
Open a page containing your component
Make sure the component is being viewed in edit mode (indicated by the highlighted pencil icon on the sidekick)
Double click on the component. Tip: the component needs something inside of it. When hovering over that something, a blue box will appear.
Enter some text
Click OK

Step 5: Confirm data is persisted
To view data, CRXDE Lite will work, but CRXDE Explorer is better.


Visit the content and page node where an instance of your component has been created. It will be under /content/(page name)/jcr:content/myComponent.

The property that we entered was for the title, which we were given the name “jcr:title”. It should have our data. The other property that we left blank, named “jcr:text”, has no entry at all. Note that without a user modifying a dialog box, component properties will have no entry.

Thanks to Sling’s RESTful model, we can view that property directly in a browser by visiting the path listed above.

http://localhost:4502/content/myPage/jcr:content/myComponent/jcr:title

But this will only work with certain property types and is not the best way to retrieve data from within components.

Step 6: Fetch properties from within a component
Within myComponent.jsp, properties can be retrieved with a call to properties.get().

String title = properties.get("jcr:title", "default title");

Full code:
<%@include file="/libs/foundation/global.jsp"%>

<%
String title = properties.get("jcr:title", "default title");
String text = properties.get("jcr:text", "default text");

//String title = properties.get("jcr:title", String.class); //defaults to null
%>

Title: <%= title %> <br/>
Text: <%= text %> <br/>


The first parameter in our properties.get a call is the property name. The second call is a default in case the property doesn’t exist. Tip: the variable “null” will not work as a second parameter. In order to retrieve nulls for nonexistent properties, you must specify the class type instead:

String title = properties.get("jcr:title", String.class);

Step 7: Fetch properties in another component
Unfortunately, the convenient properties.get method will only work within a component when extracting its own properties. In order to get another component’s properties, first, we’ll need a node reference to an instance of it.

Option 1
The most direct way is with a hardcoded reference to the object, and use of the resourceResolver class:

String componentPath = "/content/myPage/jcr:content/myComponent"; //path to component
Node node = resourceResolver.getResource(componentPath).adaptTo(Node.class);


Note that we must reference a created instance of the component, not the component itself. For instance, this will not work:

String componentPath = "/apps/matelli/components/content/myComponent";

The above will not work because properties are not stored on the component itself, but rather a content node whose sling:resourceType points to that component.

Option 2
There are risks to hardcoding that component path. If the component doesn’t exist, for instance, we’ll get an error while trying to convert it to a node. We can add some code to work around that if we want:

Resource myResource = resourceResolver.getResource(componentPath);
String title = "", text= "";
if (!Resource.RESOURCE_TYPE_NON_EXISTING.equals(myResource.getResourceType())) {
Node node = myResource.adaptTo(Node.class);
title = node.getProperty("jcr:title").getString();
text= node.getProperty("jcr:text").getString();
}


Option 3
We could also be more descriptive of our componentPath reference, and use a path relative to a page.

Page myPage = currentPage; //reference to whatever page contains the component";
String componentPath = myPage.getPath() + "/jcr:content/myComponent"; //path to component


Note that unless the component is embedded into the page via a cq:include block, we will want to combine this with the above option and check for the existence of the resource.

Option 4
The Page class has a method for retrieving nodes within “jcr:content” that’s a little cleaner:

Node node = myPage.getContentResource("myComponent").adaptTo(Node.class);

Again, developers may want to combine this method with the ones above that check for a resource’s existence before adapting it to a Node.

Now that we have a node, we can extract properties from that node.

String title = node.getProperty("jcr:title").getString();

Unfortunately, there’s no easy way to specify a property type or default value. To get other property types, you can use the Property api and fetch other types of data off the Value object.

Calendar date = node.getProperty("myDateProperty").getValue().getDate();

Tip: There’s also a convenience class to help with type extraction of other natives: PropertiesUtil

<%@page import="org.apache.sling.commons.osgi.PropertiesUtil" %>
<% Boolean myProperty = PropertiesUtil.toBoolean(context.getProperties().get("myproperty"), false); %>


However we convert a property, if it does not first exist, we will still get an error. Therefore, we should check with the hasProperty method first. Altogether, it would look like this:

String title = node.hasProperty("jcr:title") ? PropertiesUtil.toString(node.getProperty("jcr:title"), "default title") : "default title";

Here is the full code, with earlier options commented out:
<%@include file="/libs/foundation/global.jsp" %>

<cq:include path="myComponent" resourceType="matelli/components/content/myComponent" />

<%
/** Option 1: Simply direct reference */
//String componentPath = "/content/myPage/jcr:content/myComponent"; //path to component
//Node node = resourceResolver.getResource(componentPath).adaptTo(Node.class);
//String title = node.getProperty("jcr:title").getString();
//String text = node.getProperty("jcr:text").getString();

/** Option 2: Direct reference, check for resource existence */
//String componentPath = "/content/myPage/jcr:content/myComponent"; //path to component
//Resource myResource = resourceResolver.getResource(componentPath);
//String title = "", title = "";
//if (!Resource.RESOURCE_TYPE_NON_EXISTING.equals(myResource.getResourceType())) {
// Node node = myResource.adaptTo(Node.class);
// title = node.getProperty("jcr:title").getString();
// text = node.getProperty("jcr:text").getString();
//}

/** Option 3: Page-relative reference */
//Page myPage = currentPage; //reference to whatever page contains the component";
//String componentPath = myPage.getPath() + "/jcr:content/myComponent"; //path to component
//String title = node.getProperty("jcr:title").getString();
//String text = node.getProperty("jcr:text").getString();

/** Option 4: Page-relative reference with getContentResource, and hasProperty check */
Node node = currentPage.getContentResource("myComponent").adaptTo(Node.class);
String title = node.hasProperty("jcr:title") ? node.getProperty("jcr:title").getString() : "default title";
String text = node.hasProperty("jcr:text") ? node.getProperty("jcr:text").getString() : "default text";
%>

Component's title: <%= title %> <br/>
Component's text: <%= text %> <br/>


Summary
AEM makes storing and fetching properties relatively easy, although it's done differently than some other systems. Properties can be fetched from anywhere, but its easiest from the component itself.



By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.