Popular Articles
Welcome on Exadel authoring Kit for AEM
-
Attributes of fields
Components Touch UI dialogs honor the concept of global HTML attributes added to rendered HTML tags. To set them via AEM-Dialog-Plugin, you use the @Attribute annotation. You can define an additional property for a Granite UI widget or overwrite an existing one with the @Property annotation. This will be rendered into an appropriate subnode under the component's <cq:dialog> or <cq:design_dialog> node and will affect the behavior of a dialog widget. A @Property has its name and value attributes. The non-blank name can be the name of the property to write into, or else can contain a relative path. The relative path can be defined in such a way that the substring before the ultimate / represents the path, and the substring after the ultimate / represents the property name. Yet another mechanism available is to specify custom properties at Java class level. This can be used: For these goals the @CommonProperties annotation is designed. It accepts similar arguments to those of @Properties annotation. Yet you can also specify the XML scope for each @CommonProperty (this exactly means: in which of the XML trees, or files, the attribute will be stored, default is .content.xml) and a relative path to the root node. See the code snippet: Pay attention to the third and forth @CommonProperty-s. Specifying the path value gives the ability to traverse to any child node of the prepared XML with use of an XPath-formatted string. @CommonProperties are rendered after the XML tree is completed. Thus, setting them provides a kind of "last-chance" alternation of your Touch UI logic (can also be used for debugging). For example, the last @CommonProperty in the sample uses the power of XPath to change size attribute of every single node where size has been set to "L". Only make sure that the path points to at least one truly existing XML node. Note that XPath parser is namespace-agnostic. That is why you need to use /root/inplaceEditing... instead of /jcr:root/cq:inplaceEditing... in the sample above.
Learn More -
Static OptionProvider setup
Several Granite/Touch UI components, such as RadioGroup or Select, facilitate selecting from a set of options. Traditionally, the options are either inlined (the ToolKit offers its @RadioButton and @Option annotations for that) or supplied via a datasource. Both ways have their limitations; the in-line options are not dynamic and potentially lead to a lot of copy-pasting across components, while the datasource pattern requires creating a datasource servlet for every occasion. The ToolKit is bundled with the OptionProvider subsystem that aims at streamlining the usage of dynamic options without programming overhead. The OptionProvider is capable of delivering options in two modes; for a static Granite component it serves as a conventional datasource and above it has a JSON-supplying servlet that allows for retrieving and updating options dynamically even after a Granite UI has already been rendered. The options managed by OptionProvider can originate from: For a static Granite UI component, OptionProvider is set up via a property of @RadioGroup or @Select (see the samples below): @OptionProvider annotation has the following properties: value - contains one or more @OptionSource objects each referring to a single data source (see below); prepend - if specified, defines one or more extra options that will be inserted in the beginning of the option list independently of items acquired via the option source(s). This may be a kind of "none" or "default" option. Each extra option string must consist of a label and a value separated with a colon (:). A value may be an empty string, in which case the option ends with the : sign. If a label or a value itself must contain a colon, it can be escaped with \. If an option with a similar value is already present, the extra option will not be added; append - same as prepend, but the extra item(s) are appended to the option list. A valid list may consist of only prepended and/or appended options without the "external" part; exclude - if specified, defines one or more options (coming from an external source) that should be skipped for the current component. A string passed must match either the option's value, or text. The matching is case-insensitive. The wildcard symbol (*) can be used in matching strings; selectedValue - if set to a string that matches the value or the label of one of the datasource options, this option will be rendered as selected by default; sorted - if set to true, options will be sorted in their labels' alphabetical order regardless of the order they arrived from JCR. However, the prepended and appended options will appear in the order they were specified by the developer and will remain at the beginning and the end, respectively. Every @OptionSource object can be specified with the following properties: value - defines the path to a List-like structure, a node tree, or a tag folder. Plain paths and path references are supported. I.e. if a value is presented like /typical/jcr/path, this exact path will be looked for. However, if given in the /some/node@attr format, the attr attribute will be retrieved from /some/node, and its value will be then assumed to be the "true" path. fallback defines a reserve path value for situations in which the address specified in value is not reachable. This may be the case when value comes from an authored parameter of a component and the component has just been created. Then fallback may present a constant alternative; attributeMembers - if specified, define one or more attributes of a node to be rendered as HTML attributes of the corresponding Granite UI entities. For example, attributeMembers = "some-jcr-attribute" will be rendered as <coral-select-item data-some-jcr-attribute="literal_value_of_this_attribute"> in HTML; textTransform - if specified, defines the way the label will be transformed before rendering; valueTransform - if specified, defines the way the value will be transformed before rendering. @OptionSource allows specifying not only a JCR path but also a standard URL for either path or fallback (note: must be a complete URL string parseable with new URL("..."). The content downloaded this the URL is expected to be a JSON entity. A JSON array becomes the list of options much the same way as JCR resource with children. A singular JSON object will be converted into a singleton list (containing one option). If the JSON structure is such that the required array is nested deeper than the "root" node, you can add a "path" to the url like http://acme.com/apis/sample.json/internal/path The "path" is defined similar to a Sling suffix: it is the trailing part of the URL after the ".json/" extension. Because an @OptionProvider supports path references apart from regular paths, the setting that says "where to look for the path" can be stored in a dialog field other than the one that actually deals with paths. Therefore, it ought to be possible to dynamically respond to a path reference change. In the real world, it may look like the following. Imagine there is a dialog field (say, a path picker) that allows you to select a data source (say, an EToolbox List). Below is a select dropdown with options coming from the Exadel Toolbox List selected in the above path picker. Here's how it may look in Java code: The facility that makes it possible to dynamically update selectable options is the DependsOn action "update-options" (see more on DependsOn actions here). It accepts any of the conventional OptionProvider params described above in its params collection.
Learn More -
Creating AEM Components with ToolKit annotations
@AemComponent is your entry point to creating component authoring interfaces, such as a Dialog, a Design dialog, or an In-place editing config. When added to a Java class, this annotation must contain generic properties of the component such as title, description, etc. Additionally, @AemComponent can contain references to other Java classes that can be referred to as “views”. If, for instance, you need editConfig, you can add the @EditConfig to the Java class where the @AemComponent annotation is already present. You can also add @EditConfig to another Java class and put a reference to that class in the views collection of @AemComponent. Take note that you need either to put an annotation such as @Dialog or @EditConfig in the same Java class as @AemComponent or add it to another class and then add the class reference to the views collection. You don't need to do both. See the code snippet below, which displays all currently supported @AemComponent properties: Pay attention to the path property. This can be set in two ways. First is the "relative" path that will be resolved from the point specified by the plugin's componentsPathBase setting. This is a common way. Otherwise, you can store an "absolute" path that will be resolved from the root of the package. The absolute path starts with jcr_root folder or any folder that goes immediately under jcr_root. Usually, it would be something like /apps/vendor/components/myComponent. By default, the plugin searches for the directory specified in path and triggers an exception if unable to find one. This is justified because a non-existing folder usually means an invalid component path (a typo in path, or some misconfiguration). However, there is a way to make the plugin create a folder in the package when missing. This can be useful for single-use components (such as Exadel Toolbox Lists' items), or components that do not need a specific HTML or JSP file. In such a case add writeMode = WriteMode.CREATE to your @AemComponent. @Dialog is used for defining component's Touch UI dialog by creating and populating <cq:dialog> node. If you specify title in @Dialog, it will override the title specified in @AemComponent. Skip this if you need to have the same title rendered for the component itself and for the dialog. Pay attention to the forceIgnoreFreshness option. When set to true, it forces the entire dialog to ignore freshness of the Granite UI form beside the dialog. This will allow any component that resides within the dialog to display its default value (if specified) regardless of whether the underlying resource is being created anew (a "fresh" one) or just being edited. Most Granite UI components don't manage default values when editing a "non-fresh" resource. Some offer their own implementation of forceIgnoreFreshness (most notably, the Select component). However, specifying forceIgnoreFreshness at the @Dialog level makes the rest behave in the way the Select does. @DesignDialog is used for defining component's Touch UI dialog by creating and populating <cq:design_dialog> node. If you specify title in @DesignDialog, it will override the title specified in @AemComponent. Skip this if you need to have the same title rendered for the component itself and for the design dialog. Both the Touch UI dialog and design dialog can have either a relative simple or a complex structure. This is why they can either have a plain "all in the same screen" display or be organized with use of nested sections, or containers. In the first case, no specific setup is required. A dialog is automatically assigned the "fixed column" style. Otherwise, a dialog can be rendered in one or more tabs, or be organized as an accordion with one or more panels. To achieve this, you need to put the @Tabs or @Accordion annotation respectively beside your @Dialog/@DesignDialog. See Laying out your dialog for details. If you wish to engage Touch UI dialog features like listeners or in-place editing (those living in <cq:editConfig> node and, accordingly, _cq_editConfig.xml file), add an @EditConfig annotation to your Java class (same as above, you can as well add the annotation to a separate class and then put the reference to this class into @AemComponent's views property). @EditConfig facilitates setting of the following properties and features: Here is a basic sample of an @EditConfig with several of the parameters specified: To specify in-place editing configurations for your component, populate the inplaceEditing property of @EditConfig annotation as follows: Note that if you use type = EditorType.PLAINTEXT, there is an additional required textPropertyName value. If you do not specify a value, the same propertyName string will be used. It’s also possible to create multiple in-place editors as in the following snippet Even more simply, you can specify the richText field to "extend" RTE configuration for a Touch UI dialog elsewhere in your project: From the above snippet, you can see that richText and richTextConfig work together fine. A configuration inherited via richText can be altered by whatever properties that are specified in richTextConfig. If you use both in the same @InplaceEditingConfig, plain values, such as strings and numbers, specified for the @Extends-ed field are overwritten by their correlates from richTextConfig. On the other hand, array-typed values (such as features, specialCharacters, formats, etc.) are actually merged. So you can design a fairly basic set of features, styles, and formats to store in a field somewhere in your project and then implement several richTextConfig-s with more comprehensive feature sets. In addition to <cq:editConfig> itself, Adobe Granite makes it possible to define some in-place editing features for the current component’s children. This is done via the <cq:childEditConfig> with the same general structure as <cq:editConfig>. It facilitates the setting of the following properties and features: Usage: To create a specific decoration tag for your widget, you need to mark your Java class with @HtmlTag and put this class to the Component's views property. Then the <cq:htmlTag> node will be added to your component's nodeset. You can use an approach similar to the one described above for creating a page properties dialog. Specify an absolute path to the JCR node that refers to the page in your @AemComponent like @AemComponent(path = "/apps/my/components/pages/page"}, or else provide a relative path. Even if your componentsPathBase points to an AEM components' root, you can traverse up and to the "pages" sibling node like this: @AemComponent(path = "../pages/page"}. Page properties dialogs are most often composed of tabs. It is a common practice for page properties dialogs to include an existing tab resource instead of enumerating components one by one. This is because tabs are often reused and overlayed across the page hierarchy. You can include an existing tab as follows: Else, you can add components to the tab as usual. Note that usually in a Page properties dialog tab components are wrapped in an additional column so that they get centered on the screen and do not span the whole of the screen width. This can be achieved through a nested column. Else you can use a nested class annotated with FixedColumns for that purpose.
Learn More