In AEM we can use the Javascript Use-API to access properties and perform some actions on them.
In this example we can see that we use properties.get
to retrieve some properties from jcr:content
:
use(function () {
var Constants = {
DESCRIPTION_PROP: "jcr:description",
DESCRIPTION_LENGTH: 50
};
var title = currentPage.getNavigationTitle() || currentPage.getTitle() || currentPage.getName();
var description = properties.get(Constants.DESCRIPTION_PROP, "").substr(0, Constants.DESCRIPTION_LENGTH);
return {
title: title,
description: description
};
});
But is there also a way to set
the properties in some way via the Use-API?
The Use-API is effectively a rather awkward facade for the Sling API. Even though, you're defining variables using the var
keyword, the underlying implementation uses the APIs you may know from Java.
In fact, the very properties
object you're using is backed by a ValueMap
, a generic map implementation that doesn't allow for content modification. It's used by Apache Sling to give programmers read-only access to resource properties.
The objects you can implicitly use in a JavaScript file through the JavaScript Use API are just Sling objects exposed via a JavaScript runtime on the Java Virtual Machine. To figure out what each one of those actually is, you can read through the documentation for cq:defineObjects
and sling:defineObjects
. This documentation pertains to the tag libraries used in global.jsp
, a base JSP script included by all JSP in AEM, they'll be the same in HTL (formerly known as Sightly). Personally, I've always found the JavaScript Use API a bit clunky compared to its Java counterparts in that the Sling API isn't documented with JavaScript in mind and translating the concepts from the javadoc is, in my opinion, a rather awkward process.
In theory, you should be able to adapt your resource to a ModifiableValueMap
and modify resources. The first bit that may be counter-intuitive is adapting a resource to a given class.
var mvm = resource.adaptTo(Packages.org.apache.sling.api.resource.ModifiableValueMap);
mvm.put("a", 42);
The Packages
object exposes various types from the AEM, Sling and JCR APIs. Frankly, I'm not sure which ones are available.
Even if this works, there are a few caveats to keep in mind.
Another hurdle you'll run across, will be the permissions - the resource resolver you use when rendering a component in AEM is usually the one associated with the request, depending on the user viewing the component. If the user can't save in the given locatoin, the operation will not succeed. It is, naturally, possible to obtain a custom resource resolver on behalf of a service user with arbitrary permissions, by using the ResourceResolverFactory
. It's just not something I'd be willing to place directly in a single file using the JS Use API.
Lastly, and perhaps most importantly, an AEM component that calls the Use API via an HTL script is not something I'd generally expect to modify the contents of the repository. Any modifications to the repository would be better encapsulated in an OSGi service. That service could then be wired up to a servlet, a workflow, a scheduled job and, if you really want, an AEM component, either through Sling Models or the Use API.
There are valid scenarios for this, like using a script to render a response to a POST request, but even then, I'd much rather use a dedicated service to encapsulate the persistence logic and only use HTL with the Use API to write presentation logic.
If you do choose to go that way, make sure to consider how that affects your ability to cache the rendered HTML, which is crucial for performance in AEM implementations. Think if there are any consequences regarding replication. If this is happening on Publish, you're entering user-generated content territory, which comes with its own set of limitations to consider.