Expand my Community achievements bar.

Don’t miss the AEM Skill Exchange in SF on Nov 14—hear from industry leaders, learn best practices, and enhance your AEM strategy with practical tips.

Creating a node in the JCR when a component is rendered

Avatar

Level 2

I am creating a component that contains an image-rendering (Java) servlet as part of its implementation. When the component is first rendered, it does not have a node under /content in the JCR, which is preventing the image from loading. What I think is happening is that since I am requesting an image from AEM based on the location of the instance of my component in the JCR, Sling cannot properly find and invoke the servlet, since no node exists with the given selectors.

Specifically:  <img src="${resource.path}.image.png?key=123" />

I haven't found a way to automatically create the instance node in the JCR, though I can create the node when I save a new instance configuration to the JCR via the component dialog. Is there some way I can have the instance node be created automatically on component render, or, otherwise, configure something on the server so that I can always make a request that will be successfully routed to my image-rendering servlet?

18 Replies

Avatar

Level 10

Isn't your component embedded in a page under /content. I am not sure what you mean when you say nothing under / content. All web pages that are rendered in AEM are under /content.

Avatar

Level 10

Or do you mean you want to create a sling servlet that renders an image? You can invoke a servlet without creating a node.

Avatar

Employee Advisor

Hi,

so do I understand correctly, that you want to render an image, which is associated with a page, but not necessarily present upon first creation (for example like a screenshot of the page)?

If that's correct, creating the image is probably to way to go. But what if you store that image then only in the dispatcher cache (instead of saving it to the repository) and serving it from there? Of course on every purge of the dispatcher cache you need to recreate it, which takes time. I would save it to the repository only if there's no other option, if rendering takes too much time and occurs to often.
But if you store it in the repo, you probably shouldn't create it on first request, but rather pre-compute it (maybe already when the page is updated). In every case you had to decouple the sessions you use: The (anonymous) session for requesting the image, and a privileged session to save the created image to the repository.

kind regards,
Jörg

Avatar

Level 2

My page has a node under /content when the page is created and rendered, but my embedded component does not. That is, my embedded component does not have its own node in the JCR until I use the dialog to configure its parameters. I don't want to require that an author use the dialog in order to make this component function properly.

The reason I am trying to render an image, rather than simply serving a static image, is that I am creating a Captcha component that developers can embed into their custom forms. I started with the Captcha component in the Form Builder tool, and this component includes an image-rendering servlet that generates a new image on every request. I want a developer to be able to include the resource in their form without forcing them to also author the page. The Captcha should function properly immediately, and any configuration should be entirely at an authors' discretion.

Thanks

Avatar

Employee Advisor

Hi,

I don't really understand your requirement. When you want to render a component, there must be a resource (!= NonExistingResource) which the proper ResourceType defined. So rendering a component without the underlying Resource is not possible.

In the opening posting you mentioned the use of a selector for the page component. This is different, because for the page you have a Resource.

kind regards,
Jörg

Avatar

Level 2

When I use "${resource.path}" in my Sightly template, I do get the correct path in the JCR to where the node _should_ be. The node does not exist, however, until I configure it via the component dialog. What I want is for the node to exist without my needing to configure the component via its dialog.

As far as "selectors" go, it is my attempt to describe what is failing to the best of my understanding. To reiterate:

  • I'm requesting an image at the URI "${resource.path}.image.png?key=123" 
  •     I imagine it is Sling that is attempting to route my request to some appropriate handler
  • The request for the image at that URI returns a 404
  •     I guess that Sling is unable to find any handler for my request
  • When I manually a node in the JCR in at the path indicated by "${resource.path}", the 404 is replaced with a 200 that contains the image as expected
  •     Apparently, the existence of a node at the correct path enables Sling to find my custom image-rendering servlet

Avatar

Level 2

Hi,

I got the same issue with my use case.

<div data-sly-resource="${ field.variable @ resourceType=field.fieldType,description=field.description, variable=field.variable,decorationTagName='div'}"></div>

It's just create node content once we set dialog property. I need it auto create node data once the line of code above run.

Why i need that?

- My selection is populate data via a Servlet which base on these information so that I need to get resourceType and node name of it to resolve data properly.

Could you give advice more for this use case? Thank you!

P/S: I'm using AEM 6.2

Regards,

Hung Vu

Avatar

Level 10

Are you trying to populate values that are displayed in a HTL front end component? 

Avatar

Level 2

Are you trying to populate values that are displayed in a HTL front end component? -> Nope

I want populate content for dialog component which base on node name and resourType.

----------------------

Thanks for your quick reply. Let me rephrase it there.

I have a page template which is using HTL. The page components will be rendered via code bellow code.

<div data-sly-resource="${ field.variable @ resourceType=field.fieldType,description=field.description, variable=field.variable,decorationTagName='div'}"></div>

It will render a list of component there. In the list of component I have a special component which is call Dropdown field.

It has dialog config look like

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
   jcr:primaryType="cq:Dialog"
   title="Dropdown field"
   xtype="panel">

   <items jcr:primaryType="cq:WidgetCollection">

   <content
   jcr:primaryType="cq:Widget"
   fieldLabel="Content Data"
   fieldDescription="Output JSON data"
   name="./content"
   options="$PATH.option.json"
   type="select"
   xtype="selection"/>

   </items>

</jcr:root>

My issue that is populate content data for this selection/dropdown in the component dialog. My servlet rely on node name and resourceType of this component to populate/resolve content data for the selection/dropdown in the dialog.

I had googled with these key words bellow but not help much.

"aem resourceType force create node"

"how to pass value to AEM widget dialog"

P/S:

Full rendering page HTL code

<div data-sly-list.field="${model.listContentField}">

   <div data-sly-test.hasData="${field}" style="margin-top: 25px">

   <fieldset>

   <legend data-sly-test="${field.required}" style="color: chocolate" data-sly-text="${field.description} || Required field"></legend>

   <legend data-sly-test="${!field.required}" data-sly-text="${field.description}"></legend>

   <div data-sly-resource="${ field.variable @ resourceType=field.fieldType,description=field.description, variable=field.variable,decorationTagName='div'}"></div>

   </fieldset>

   </div>

</div>

Regards & Thanks,

Hung Vu

Avatar

Employee Advisor

Ok, I think I got it.

Your problem is indeed the non-existence of the underlying resource. if there's no resource, Sling cannot resolve any script. So it's all based on the existence of this resource.

But you can build it like this:

* ${page}.image.jpg will display the image (if present) or do any other operation you want.

The difference is that you build the "image" selector as a selector the page, and not as a selector to your captcha/image component. Because the page resource is always there, resolving the script for the "image" selector will succeed as well. And then you can register a servlet to react on this selector and implement it as you like.

kind regards,
Jörg

Avatar

Level 2

Thank you, kindly, Jörg. I will try this and see if I can get it to work.

Avatar

Level 1

If you want your component to have some nodes created automatically you have to make use of cq:template. You create it beside cq:dialog. If you want to have node called image_node created automatically, your cq:template would look something like this:

<jcr:root jcr:primaryType="nt:unstructured" ...>

    <image_node jcr:primaryType="nt:unstructured" sling:resourceType="path/to/component/definition"/>

</jcr:root>

Avatar

Level 2

Thanks @igorr76272295

For my case. It won't work as expected with data-sly-resource.

Note that there are we have two use case using of a component.

#1: We use sly API to give a component (this is my case)

<div data-sly-resource="${ childField.path @ resourceType=childField.fieldType, description=childField.description, variable=childField.variable, decorationTagName='div'}"></div

>

#2: Content author will drag & drop component from sidekick into the existing pars in a page.

P/S: It works in case we drag & drop component into the parsys.

Regards & Thanks,

Hung Vu

Avatar

Level 1

As you said, cq:template node of the component has the desired effect only in case author adds the component to page - true. For the first use case, however, you have to include the respective nodes in the template of your page, e.g.,:

<jcr:root jcr:primarytype="cq:Template" ...>

     <jcr:content jcr:primaryType="cq:PageContent" ...>

          <your_component sling:resourceType="path/to/your/component">

               <image_node jcr:primaryType="nt:unstructured" sling:resourceType="path/to/component/definition"/>

          </your_component>

     </jcr:content>

</jcr:root>

Avatar

Level 2

Hi @igorr76272295,

I got what you mean. Your case is populating component as static via template configuration with pre-define node name and resourceType for that node.

It's not my case. My components are generated dynamically.

My real use case:

- I have a page configuration which config node name and resourceType for a component to be render in other page

- I have another page which use sly to render the configuration components which are configured above. That is reason you saw my sample code which has a Model to get list components which are configured from other page.

Thank you!,

Regards,

Hung Vu

Avatar

Employee Advisor

That means that your constraints do not allow you to create a template with the proper components already contained, but you rather need to determine the name and resource type when that page is being created (based on some data stored somewhere in the JCR)?

TBH that's a rather unusal requirement. The problem is that at the the moment you create the page this data influences the name and (more important) the resourcetype of that component; but I don't see any way to change it afterwards; and at some point you will have the requirement to do that. Also you need to customize a lot of make this really happen (and creating the nodes on first rendering should never be an option ...).

If you cannot determine the exact type of component during creation time I would rather create a proxy component, which can be configured by JCR properties; and then based on the configuration it behaves like the components you want it to be (either by implementing a huge switch statement or by rather doing a sling:include with the proper resourcetype set).

Jörg

Avatar

Level 1

In that case you have to use Dynamic Templates that were introduced in AEM 6.2. Have a look here -> Creating Page Templates

Avatar

Level 2

Thanks @igorr76272295Jörg Hoh

My business has the same idea which Creating Page Templates does, but it doesn't match 100% our use case. My business can map with Adobe Experience Manager Help | Using Content Fragments in AEM​.

My business reqquirement

#1: Create a template which can define list of field for a Form

#2: Create another template which its page content will point to page content which is created by template above (#1).

#3: We can authoring for page content which is created at (#2)

#4: We expose an API to export data as JSON

Fortunately, during debug I got node name of editing component which base on request path of component request (without node is created), so that i can solve my issue.