Creating a Composite Multifield in AEM
Step 1: Extending the Dialog Box for the Profile Component
- Location: Start by navigating to the path
ui.apps/src/main/content/jcr_root/apps/wknd/components/profile
in your AEM project using IntelliJ. - Dialog Box Extension:
- Add a new node under the existing dialog definition for the “Profile” component.
- Name this node
previousExperience
and set itssling:resourceType
to"granite/ui/components/coral/foundation/form/multifield"
. - Inside this node, create a
field
node withsling:resourceType
as"granite/ui/components/coral/foundation/form/fieldset"
. - Under the
field
node, add nodes for each subfield:companyName
,startDate
, andendDate
. Set their respectivesling:resourceType
to the appropriate field types (textfield
forcompanyName
anddatepicker
forstartDate
andendDate
).
<!-- Previous Experience Multifield -->
<previousExperience
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/multifield"
composite="{Boolean}true"
fieldLabel="Previous Experience"
>
<field
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container"
name="./prevExperiences">
<items jcr:primaryType="nt:unstructured">
<companyName
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldLabel="Company Name"
name="./companyName"/>
<startDate
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/datepicker"
fieldLabel="Start Date"
name="./startDate"/>
<endDate
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/datepicker"
fieldLabel="End Date"
name="./endDate"/>
</items>
</field>
</previousExperience>
Step 2: Sling Model Creation
Profile Component Sling Model:
package com.adobe.aem.guides.wknd.core.models;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ChildResource;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
import java.util.List;
@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class ProfileModel {
@ValueMapValue
private List<String> hobbies;
@ChildResource
private List<Experience> previousExperience;
public List<Experience> getPreviousExperience() {
return previousExperience;
}
public List<String> getHobbies() {
return hobbies;
}
}
Package Declaration:
package com.adobe.aem.guides.wknd.core.models;
- This statement declares that the class belongs to the package
com.adobe.aem.guides.wknd.core.models
.
Imports:
- The import statements load various classes and interfaces that are used in the
ProfileModel
class.
Class Annotation (@Model
):
@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
- This is an annotation from the Sling Models framework. It indicates that this class is a Sling Model that can be adapted from a
Resource
. ThedefaultInjectionStrategy
beingOPTIONAL
means that if a field cannot be injected, it will be ignored rather than causing an error.
Class Definition:
public class ProfileModel { ... }
- This defines the
ProfileModel
class.
Fields:
@ValueMapValue private List<String> hobbies;
- This field represents a list of hobbies. The
@ValueMapValue
annotation is used to inject the value from the resource’s ValueMap (a key-value map of properties).
- This field represents a list of hobbies. The
@ChildResource private List<Experience> previousExperience;
- This field represents a list of
Experience
objects. The@ChildResource
annotation is used to inject child resources. Here, it’s expected that theExperience
objects are child nodes of the current resource in the JCR (Java Content Repository).
- This field represents a list of
Getters:
- These are methods to access the values of the fields:
public List<Experience> getPreviousExperience() { return previousExperience; }
public List<String> getHobbies() { return hobbies; }
- These methods allow other parts of the AEM application to access the
hobbies
andpreviousExperience
properties.
Experience Component Sling Model:
package com.adobe.aem.guides.wknd.core.models;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
//Inner Class for Experience
@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class Experience {
@ValueMapValue
private String companyName;
@ValueMapValue
private String startDate;
@ValueMapValue
private String endDate;
// Getters
public String getCompanyName() { return companyName; }
public String getStartDate() { return startDate; }
public String getEndDate() { return endDate; }
}
This code defines a Java class named Experience
, which is part of the com.adobe.aem.guides.wknd.core.models
package. It is structured as a Sling Model for use in Adobe Experience Manager (AEM). Sling Models facilitate the mapping of Java objects to content stored in AEM’s Java Content Repository (JCR). Let’s break down the components of the Experience
class:
- Package Declaration:
package com.adobe.aem.guides.wknd.core.models;
- Specifies that the class is part of the
com.adobe.aem.guides.wknd.core.models
package.
- Imports:
- The code imports necessary classes and interfaces from the Sling API and Sling Models framework.
- Class Annotation (
@Model
):@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
- This annotation marks the class as a Sling Model, meaning it can be adapted from a Sling
Resource
. ThedefaultInjectionStrategy
set toOPTIONAL
indicates that if some fields can’t be injected with values, the model will still be created without throwing an error.
- Class Definition:
public class Experience { ... }
- Defines the
Experience
class.
- Fields (Properties):
@ValueMapValue private String companyName;
- This field stores the company name. The
@ValueMapValue
annotation is used to inject the value from the resource’s ValueMap (a map of the property values).
- This field stores the company name. The
@ValueMapValue private String startDate;
- Stores the start date of the experience. Again,
@ValueMapValue
is used for injection.
- Stores the start date of the experience. Again,
@ValueMapValue private String endDate;
- Stores the end date of the experience.
- Getters:
public String getCompanyName() { return companyName; }
public String getStartDate() { return startDate; }
public String getEndDate() { return endDate; }
- These methods are getters that provide access to the values of the respective fields. They are used to retrieve the company name, start date, and end date of an experience.
In summary, the Experience
class in this code is designed as a Sling Model in AEM. It represents an ‘experience’ entity with properties like company name, start date, and end date, which are injected from a Sling Resource. This class is likely used in conjunction with other models to represent a user’s professional experiences in an AEM application.
Step 3: Write Sightly Code
- HTL Code for Profile Component:
- Use the
data-sly-use
attribute to instantiate the Profile Sling Model. - Render the basic profile information as you have already been doing.
- For the “Previous Experience” section, use a
data-sly-list
to iterate overpreviousExperiences
. - Inside the loop, render
companyName
,startDate
, andendDate
for eachExperience
object.
- Use the
<img src="${properties.profilePicture}" alt="Profile Picture">
<h2>${properties.candidateName}</h2>
<p>Date of Birth: ${'dd-MM-yyyy' @ format=properties.dateOfBirth}</p>
<p>Engineering Major: ${properties.engineeringMajor}</p>
<p>Gender: ${properties.gender}</p>
<p>Hobbies Listed Below:</p>
<div data-sly-use.profileModel="com.adobe.aem.guides.wknd.core.models.ProfileModel">
<!-- Existing fields here -->
<ul data-sly-list.hobby="${profileModel.hobbies}">
<li>${hobby}</li>
</ul>
<ul data-sly-list.exp="${profileModel.previousExperience}">
<li>${exp.companyName}</li>
</ul>
</div>
Step 4: Testing the Component
- Deploy the Code: Deploy your changes to AEM.
- Open the AEM Authoring Environment: Navigate to a page where the Profile component is used.
- Edit the Component: Open the dialog box for the Profile component.
- Add Previous Experience Data: Use the multifield to add previous experience details.
- Save and Preview: Save the dialog and preview the page to see the rendered output.
Flow of Data from Backend to Frontend
- Dialog Box: The author enters data in the dialog box fields.
- Sling Model: When the page is requested, AEM instantiates the Profile Sling Model, injecting the dialog data into the model.
- HTL Rendering: The HTL script uses the Profile Sling Model to access and render the data on the webpage.