Thursday, 12 November 2015

Fetching 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 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”
make_component
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.
dialog_node
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:
dialog_properties
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:
include_component
<cq:include path="myComponent" resourceType="matelli/components/content/myComponent" />
Tip: component properties cannot be set dynamically the way other platforms, like mxml, allow.
include_component_not_mxml
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.
edit_my_component1
Be sure you’re logged into an author environment with a permissioned 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.
edit_my_component2
Enter some text
Click OK
Step 5: Confirm data is persisted
To view data, CRXDE Lite will work, but CRXDE Explorer is better.
view_component_property
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 was 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");
<%@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/>
view rawmyComponent.jsp hosted with ❤ by GitHub
The first parameter in our properties.get 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 a 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.  All together, 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/>
view rawmyPage.jsp hosted with ❤ by GitHub

AEM Templates
calling the method getTemplate() will return null on publish systems.
Try this:

if (currentPage.getProperties().get("cq:template""").equals(MyTemplate) {

    %>temp = my template<%
} else {
    %>temp not equal to my template<%
    }



Using currentPage.getTemplate() on CQ 5.5 on publish systems will return null. The reason is that since CQ 5.5 the user anonymous has no read rights on /apps. So in my opinion the better way is to read the property cq:template from the current page.

No comments :

Post a comment