Expand my Community achievements bar.

Guidelines for the Responsible Use of Generative AI in the Experience Cloud Community.
SOLVED

AEM authoring dialog - duplicate saved property value with different name

Avatar

Level 3

I've got the below textfield that authors use to input a property.

This gets saved in JCR as articleId.

<articleId
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldLabel="Article Id"
name="./articleId"/>

I would like to create another property and save the same articleId value against it(cq:panelTitle in my case). Essentially maintaining two properties with the same value.

Trying to prevent the authors from filling an additional field.

How do i achieve this during dialog save ?

 

Screen Shot 2022-05-20 at 12.44.25 pm.png

 

Thanks.

1 Accepted Solution

Avatar

Correct answer by
Employee Advisor

Another way we can achieve this also , I am sharing touch ui dialog editor .context.xml here -

 

 

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="nt:unstructured"
jcr:title="Application"
sling:resourceType="cq/gui/components/authoring/dialog">
<content
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns">
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<articleId
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldLabel="Article Id"
name="./newarticleId"/>
<panelTitle
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/hidden"
defaultValue="./articleId"
fieldLabel="CQ Article Panel"
name="./cq:panelTitle@ValueFrom"
value="./articleId"/>
</items>
</column>
</items>
</content>
</jcr:root>

When a new value is saved for articleId, Sling will update automatically cq:panelTitle property via @ValueFrom suffix. It’s also important to use granite/ui/components/coral/foundation/form/hidden widget in order to work properly. 

 

Only this much will be required at dialog editor only and we don't need anything.

 

Reference link: https://www.flexibledesigns.rs/automatic-update-of-a-dialog-field-based-on-the-value-of-another-fiel...

 

View solution in original post

7 Replies

Avatar

Employee Advisor

I have created below sample component for the same -

 

Here is .content.xml for touch ui dialog editor 

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
    jcr:primaryType="nt:unstructured"
    jcr:title="Application"
    sling:resourceType="cq/gui/components/authoring/dialog">
    <content
        jcr:primaryType="nt:unstructured"
        sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns">
        <items jcr:primaryType="nt:unstructured">
            <column
                jcr:primaryType="nt:unstructured"
                sling:resourceType="granite/ui/components/coral/foundation/container">
                <items jcr:primaryType="nt:unstructured">
                    <articleId
                        jcr:primaryType="nt:unstructured"
                        sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                        fieldLabel="Article Id"
                        name="./articleId"/>
                    <panelTitle
                        jcr:primaryType="nt:unstructured"
                        sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                        disabled="{Boolean}true"
                        fieldLabel="CQ Article Panel"
                        name="./cq:panelTitle"/>
                </items>
            </column>
        </items>
    </content>
</jcr:root>

 

 

I have two text fields here and I just made ./cq:panelTitle disable so, author can't change the value of this field.

 

Sling model -

 

package com.aem.demo.core.models;

import java.util.Objects;

import javax.annotation.PostConstruct;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.ValueFormatException;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.version.VersionException;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.SlingObject;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

@Model(adaptables = SlingHttpServletRequest.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class ArticleModel {

	
	@ValueMapValue
	private String articleId;

	@ValueMapValue (name = "cq:panelTitle")
	private String panel;

	
	@SlingObject
	SlingHttpServletRequest slingHttpServletRequest;
	
	@PostConstruct
	protected void init() {
        if (Objects.nonNull(articleId)) {
        	
        	panel = articleId;
    		Node currentnode = slingHttpServletRequest.getResource().adaptTo(Node.class);

    		Session session = slingHttpServletRequest.getResourceResolver().adaptTo(Session.class);
    		try {
    			currentnode.setProperty("cq:panelTitle", panel);
    			session.save();
    		} catch (ValueFormatException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (VersionException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (LockException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (ConstraintViolationException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (RepositoryException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
			
		}
		

	}

	/**
	 * @return the articleId
	 */
	public String getArticleId() {
		return articleId;
	}

	/**
	 * @return the panel
	 */
	public String getPanel() {
		return panel;
	}

}

 

 

Here I am reading the value of articleId and assign the same to cq:PanelTitle. This sling model is also helping to set the assigned value to cq:PanelTitle and store the same to aem repository as shown below -

 

DEBAL_DAS_0-1653032438749.pngDEBAL_DAS_1-1653032519305.png

HTL code to display the value-

ARTICLE
<sly data-sly-use.article="com.aem.demo.core.models.ArticleModel"></sly>

        <h3>${article.articleId}</h3></br>
        <h3>${article.panel}</h3>  

</div>

Hope this will help.

 

Avatar

Employee Advisor

we can hide cq:panelTitle field as well as shown below, if you don't want to show it to content authors -

 

DEBAL_DAS_0-1653032861985.png

The below sling model , htl code will be working as expected in this case also.

Avatar

Community Advisor

Hi @subsul1,

I think that the easiest option will be to create client lib with below js code that will simply copy articleId value into cq:panelTitle before dialog submit. Here is js you can use.

(function ($, $document) {
	"use strict";

	$(document).on("click", ".cq-dialog-submit", function (e) {
        var articleIdValue = $("[name='./articleId']").val();
	var cqPanelTitle = $("[name='./cq:panelTitle']").attr("value", articleIdValue);
    });

})($, $(document));

this assumes dialog structure like this:

<items jcr:primaryType="nt:unstructured">
  <articleId
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
    fieldLabel="Article Id"
    name="./articleId"/>
  <panelTitle
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
    disabled="{Boolean}true"
    fieldLabel="CQ Panel Title"
    name="./cq:panelTitle"/>
</items>

Please remember to add extraClientlibs property in your dialog definition that will point to the clientlib with js definition.

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
    jcr:primaryType="nt:unstructured"
    jcr:title="Dialog title"
    extraClientlibs="[your.client.lib.category]"
    sling:resourceType="cq/gui/components/authoring/dialog">

You can also consider to use hidden filed for cq:panelTitle if you do not want to expose this filed to the author.

 

Alternatively you can create https://sling.apache.org/apidocs/sling5/org/apache/sling/servlets/post/SlingPostProcessor.html to handle it on backend side.

In my opinion you should avoid changing data on Sling Model level. Sling Model is to read/map data from repository.

Avatar

Level 3

@lukasz-m Thanks for taking time to answer my question. @DEBAL_DAS 's answer I feel is straight forward and does not require inclusion of extraClientlibs.

Anyway, your answer has helped me learn some more concepts within AEM. Thanks.

Avatar

Correct answer by
Employee Advisor

Another way we can achieve this also , I am sharing touch ui dialog editor .context.xml here -

 

 

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="nt:unstructured"
jcr:title="Application"
sling:resourceType="cq/gui/components/authoring/dialog">
<content
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns">
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<articleId
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldLabel="Article Id"
name="./newarticleId"/>
<panelTitle
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/hidden"
defaultValue="./articleId"
fieldLabel="CQ Article Panel"
name="./cq:panelTitle@ValueFrom"
value="./articleId"/>
</items>
</column>
</items>
</content>
</jcr:root>

When a new value is saved for articleId, Sling will update automatically cq:panelTitle property via @ValueFrom suffix. It’s also important to use granite/ui/components/coral/foundation/form/hidden widget in order to work properly. 

 

Only this much will be required at dialog editor only and we don't need anything.

 

Reference link: https://www.flexibledesigns.rs/automatic-update-of-a-dialog-field-based-on-the-value-of-another-fiel...

 

Avatar

Level 3

@DEBAL_DAS Thanks for taking time to answer my question. @ValueFrom approach is succinct and does the job very well. 

Got to learn a few concepts from your answers. Thank you.

Avatar

Community Advisor

hi,

Why do you want to save same value in two properties? It is not good for maintaining purpose.

What do you want to achieve with this? There could be an another way.



Arun Patidar