I have a component that needs to fetch properties from another component on the same page.
Is there a way to get a component's NODE object from currentPage ?
I have the name of the node I need to fetch available in the code.
Assuming the node you need is in Page/jcr:content/yournode:
the Page object has a method getContentResource() which returns you the jcr:content resource node by default. You can also use page.getContentResource("yournode") to get a specific node below jcr:content.
If your node, for some reason is sibling to jcr:content (it shouldn't btw), you can your iterate the children of a resource using resource.listChildren().
Remember, this is all Sling API, so you are managing resources, not nodes. You can get a JCR Node from a resource using resource.adaptTo(Node.class)
Usually every Page class has a method for retrieving nodes within jcr:content that’s a little cleaner:
Node node = CurrentPage.getContentResource("COMPONENTNAME").adaptTo(Node.class);
Developers should 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();
This way you can fetch any properties of a component.
I would like to add that this can be solved in more clean way by delegating request to a servlet provided the problem being solved requires separation of concerns - separate the view from the controller logic.
LINK EXPLAINING IN DETAIL HOW VALIDATION IS DONE USING SLING SERVLETS
Adds validator to a form using below logic that calls the sling servlet hosted
var url = CQ.HTTP.addParameter(dialog.path + '.validator.json', 'value', value);
var result = CQ.HTTP.eval(url);
and in the servlet we access the node and its parents using below logic
final Node currentNode = request.getResource().adaptTo(Node.class);
try {
final Node par = currentNode.getParent();
final NodeIterator nodes = par.getNodes();
// get all form names for the current paragraph system
while (nodes.hasNext()) { //and the business logic
See more at: http://www.citytechinc.com/us/en/blog/2012/04/complex_cq5_dialog_validation.html#sthash.wey3cwdI.dpuf
Related
I’m trying to figure out if it’s possible to customize the name of the node AEM creates when I first drop a component on the page.
The cq:Component node where my component is defined is named “knowledge-center-question” and when I drop it, AEM creates a node named “knowledge_center_que” in the page’s node tree using its default naming logic. I would prefer for the node name to be “question” when it is dropped (but I’d rather not rename the component itself).
It seems like this kind of thing must be possible given how customizable everything is in AEM, but I’m struggling to find an answer.
Take a look at :nameHints, which can be send as POST arguments to the SlingPostServlet: https://sling.apache.org/documentation/bundles/manipulating-content-the-slingpostservlet-servlets-post.html#algorithm-for-node-name-creation
You need to write a custom Sling post processor. Sling post processor is called after a component dropped in the page. Example code :
#Component(service = SlingPostProcessor.class, immediate = true, name = "com.aem.CustomPostProcessor")
public class CustomPostProcessor implements SlingPostProcessor {
#Override
public void process(SlingHttpServletRequest request, List<Modification> modifications) throws Exception {
if (accepts(request)) {
final Resource resource = request.getResourceResolver().getResource(request.getResource().getPath());
// Your logic
modifications.add(Modification.onCreated(resource.getPath()));
}
}
protected boolean accepts(SlingHttpServletRequest request) {
return "/my/resource/type".equals(request.getResource().getResourceType());
}
}
I'm trying to set up a system with multiple applications connecting by use of a discovery server. I can traverse the hal responses to a specific resource, but I'm looking for a solution to get from a collection resource to a single resource and find the data for a specific entity.
In 1 application I have a RepositoryRestResource exposing some object:
#RestRepositoryResource(collectionResourceRel="things", itemResourceRel="thing") public interface ThingRepo extends CrudRepository<Thing,Long> {}
In some other application, I would like to get to a single thing. I have the id (let's say it's 1) and have the relation name of the collection and the single resource.
I would like to use a DiscoveredResource to get a link to this single item resource, or to the collection resource which I can then somehow expand using the ID (which would require a templated resource).
If at all possible I would not like to just add "/1" at the end of the URL.
this is how I currently create a DiscoveredResource to point to the collection resource:
new DiscoveredResource(new DynamicServiceInstanceProvider(discoveryClient, traverson -> traverson.follow("things"));
Should I and is it possible to add a templated link on a collection resource created by a #RepositoryRestResource. Or is there some other trick I am missing?
The solution here is to add a custom method as a #RestResource which exposes a relation with a templates URL you can then follow to.
Repo:
#RestRepositoryResource(collectionResourceRel="things", itemResourceRel="thing") public interface ThingRepo extends CrudRepository<Thing,Long> {
#RestResource(rel = "thing")
Thing findOneById(#Param("id") Long id);
}
Discovery + traverson:
DiscoveredResource resource = new DiscoveredResource(new DynamicServiceInstanceProvider(discoveryClient, traverson -> traverson.follow("things","search","thing"));
Link link = resource.getLink().expand(id);
I am currently using the ServicePartitionResolver to get the http endpoint of another application within my cluster.
var resolver = ServicePartitionResolver.GetDefault();
var partition = await resolver.ResolveAsync(serviceUri, partitionKey ?? ServicePartitionKey.Singleton, CancellationToken.None);
var endpoints = JObject.Parse(partition.GetEndpoint().Address)["Endpoints"];
return endpoints[endpointName].ToString().TrimEnd('/');
This works as expected, however if I redeploy my target application and its port changes on my local dev box, the source application still returns the old endpoint (which is now invalid). Is there a cache somewhere that I can clear? Or is this a bug?
Yes, they are cached. If you know that the partition is no longer valid, or if you receive an error, you can call the resolver.ResolveAsync() that has an overload that takes the earlier ResolvedServicePartition previousRsp, which triggers a refresh.
This api-overload is used in cases where the client knows that the
resolved service partition that it has is no longer valid.
See this article too.
Yes. They are cached. There are 2 solutions to overcome this.
The simplest code change that you need to do is replace var resolver = ServicePartitionResolver.GetDefault(); with var resolver = new ServicePartitionResolver();. This forces the service to create a new ServicePartitionResolver object to every time. Whereas, GetDefault() gets the cached object.
[Recommended] The right way of handling this is to implement a custom CommunicationClientFactory that implements CommunicationClientFactoryBase. And then initialize a ServicePartitionClient and call InvokeWithRetryAsync. It is documented clearly in Service Communication in the Communication clients and factories section.
I have a Sling Resource object. What is the best way to set or update its property?
It depends on the Sling version:
Sling >= 2.3.0 (since CQ 5.6)
Adapt your resource to ModifiableValueMap, use its put method and commit the resource resolver:
ModifiableValueMap map = resource.adaptTo(ModifiableValueMap.class);
map.put("property", "value");
resource.getResourceResolver().commit();
Sling < 2.3.0 (CQ 5.5 and earlier)
Adapt your resource to PersistableValueMap, use its put and save methods:
PersistableValueMap map = resource.adaptTo(PersistableValueMap.class);
map.put("property", "value");
map.save();
JCR API
You may also adapt the resource to Node and use the JCR API to change property. However, it's a good idea to stick to one abstraction layer and in this case we somehow break the Resource abstraction provided by Sling.
Node node = resource.adaptTo(Node.class);
node.setProperty("property", "value");
node.getSession().save();
As many developers are not fond of using Node API. You can also use ValueMap and ModifiableValueMap APIs to read and update property respectively.
Read Value through ValueMap
ValueMap valueMap = resource.getValueMap();
valueMap.get("yourProperty", String.class);
Write/Modify property through ModifiableValueMap
ModifiableValueMap modifiableValueMap = resource.adaptTo(ModifiableValueMap.class);
modifiableValueMap.put("NewProperty", "Your Value"); //write
modifiableValueMap.put("OldProperty", "Updated Value"); // Modify
After writing property to a node, save these values by committing the 'resourceResolver'
You'll need system/service user for admin resourceResolver.
Go through this documentation for more info about service user.
It is not working in publish. But if user logged-In as admin it will work.
ModifiableValueMap map = resource.adaptTo(ModifiableValueMap.class);
map.put("property", "value");
resource.getResourceResolver().commit();
Our setup has various websites and some of which are livecopies from the main site. We are trying to determine if the page we are on is a livecopy. If so try and get its parent and the children of the parents. This allows us to determine each pages siblings to then use how we want.
Is this easily achievable using cq?
Checking if the page is a live copy
You can use LiveRelationshipManager, adaptable from resource resolver:
resourceResolver.adaptTo(LiveRelationshipManager.class)
It has method hasLiveRelationship which will return true if passed resource is a live copy of something other. You can invoke this method passing current component resource.
Parent and siblings
Use PageManager and Page methods:
// resource - current component resource
ResourceResolver resolver = resource.getResourceResolver();
PageManager pageManager = resolver.adaptTo(PageManager.class);
Page currentPage = pageManager.getContainingPage(resource);
Page parentPage = currentPage.getParent();
Iterator<Page> siblings = parentPage.listChildren();