AEM 6 sightly: How to read variable from language file? - aem

I have the following html:
<div >${'foo' # i18n}</div>
In my i18n file I have the following:
<foo
jcr:mixinTypes="[sling:Message]"
jcr:primaryType="sling:MessageEntry"
sling:key="foo"
sling:message="This is dummy text"/>
This is dummy text is displayed in the page. well so far. The problem is that foo is a variable which comes form other template and I read as follow:
${fooValue} //this returns foo
Now to read message from the i18n I tried the following:
<div>${'${fooValue}' # i18n} </div>
but This displays ${fooValue} in the page. How to read message from the i18n, if I have variable key?

You could use a local template to which you pass the variable identifying your key for the i18n dictionary:
<template data-sly-template.locale="${# key}">
${key # i18n, locale='de'}
</template>
<div data-sly-call="${locale # key='world'}"></div>
Assuming your i18n dictionary has the translation, the output would be:
<div>
Welt
</div>
You can also call the locale template from another template in your page:
<template data-sly-template.locale="${# key}">
${key # i18n, locale='de'}
</template>
<template data-sly-template.translate="${# string}">
<div data-sly-call="${locale # key=string}" data-sly-unwrap></div>
</template>
<div data-sly-call="${translate # string='world'}"></div>
The output would be the same as above.

The solution by Radu works wonderfully. I have also tried this and this works too, if anyone is looking for a solution without involving templates.
<div>${fooValue # i18n}</div>
I personally would go with templates though. Makes it a little easier to read.
<div>${fooValue # i18n}</div> vs <div data-sly-call="${translate # string=fooValue}"></div>.

Related

AEM different ways to use HTL sly

What is the difference between to use:
<div data-sly-resource="${'foo' # resourceType="var"} ... ></div>
or
<sly data-sly-resource="${'foo' # resourceType="var"} ... ></sly>
or
<sly data-sly-resource="${'foo' # resourceType="var"} ... />
And when I should use data-sly-unwrap?
I think the best way to understand when to use what, is to understand the fundamentals of HTL.
Most of HTL can be written around existing HTML tags and attributes, such as
<div data-sly-resource="${'foo' # resourceType='var'}" ... ></div>
However, in instances where you don't want the HTML element to be present in the output, you can leverage the sly element. When an sly element is used it is removed from the final rendered HTML automatically.
For e.g.,
<sly data-sly-resource="${'foo' # resourceType='var'}" ... ></sly>
or
<sly data-sly-resource="${'foo' # resourceType='var'}" ... /> both output the same HTML which is directly including the output of the resource foo without adding any extra tags.
But if the same was written using an HTML element such as the following
<div data-sly-resource="${'foo' # resourceType=""var}" ... ></div>
the generated HTML will contain a parent div tag around the included resource's output as shown below
<div>HTML output of foo goes here</div>
The data-sly-unwrap comes into play in such scenarios. Adding the data-sly-unwrap to an HTML element will exclude that element from the generated output, but would output all the child HTML.
For e.g., <div data-sly-resource="${'foo' # resourceType='var'}" ... data-sly-unwrap></div> and <sly data-sly-resource="${'foo' # resourceType='var'}"></sly> and <sly data-sly-resource="${'foo' # resourceType='var'}" /> would produce the same output.
Please note that the data-sly-unwrap on a sly element has no effect as the sly is removed by the processor anyways.
However, I think the power of data-sly-unwrap lies when using it with conditional statements.
For e.g., if a parent element needs to present only in certain scenarios, then using it in the following way is much cleaner than writing multiple if conditions as we used to do in JSPs or JSTLs.
<div class="parent" data-sly-unwrap="${hideParent}">
<img class="img" src="../../xyz.jpg" />
</div>
More on data-sly-unwrap here.

How to use onclick method inside AEM component

Am having a AEM6 html component, am getting the values from dialog and using it inside the component via the .js file and using the return properties.
I could able to get the authored values but it is getting null or empty when am using it inside the onclick method. Please find below the code snippet below.
<div data-sly-unwrap data-sly-use.test="test.js"></div>
<a href="#" class="${test.testId}" id="${test.testId}" onClick="toggleDraw('${test.testId}')" >
The content I authored is getting displayed in class and Id, but it is not displaying in the onClick method.
Below is the Output am getting after authoring.
<a href="#" class="get-a-quote" id="get-a-quote" onClick="toggleDraw('')" >
Output I needed is :
<a href="#" class="get-a-quote" id="get-a-quote" onClick="toggleDraw('get-a-quote')" >
This should do the trick:
<a data-sly-test.variable123="toggleDraw('${test.testId}')" href="#" class="${test.testId}" id="${test.testId}" onclick="${variable123 # context='attribute'}" >
You need to put the function call in a variable because of the nested single quotes. And you need to manually set the context in this case. If "attribute" does some escaping you do not like, you could use "unsafe" - this will end in all escaping mechanisms being disabled. That might or might not be a security issue for your application.
HTH

AEM - data-sly-resource children html

I want to figure out a way to insert HTML inside a <sly data-sly-resource> tag and be able to go inside the component I am retrieving from the resource attribute.
To compare, it would be something like Vue's slots, and React's { this.props.children }.
Example:
Parent Component
<sly data-sly-resource="${'example' # resourceType='path/to/component/structure/example'}">
<h1>Hello World</h1>
</sly>
Example Component
<div id="example-component">
${ variable.getChildrenHTMLCall() } // Does something like this exist?
</div>
Output
<div id="example-component">
<h1>Hello World</h1>
</div>
This functionality does not exist.
You could make a similar functionality by using data-sly-template. But you’d have to pass the HTML sting as a parameter (more specifically option) but that might not be desired or maintainable.
You could use the com.day.cq.contentsync.handler.util.RequestResponseFactory as seen here: http://www.nateyolles.com/blog/2015/10/get-rendered-html-for-an-aem-resource-or-component

How to set variable from use object in sightly?

I have plain use class which contains one method that returns complex bean with a lot of setters/getters. Lets name it SomeUse. Given sightly file:
<sly data-sly-use.someUse="com.mycompany.SomeUse">
${someUse.data.firstProperty}
<div>
${someUse.data.secondProperty}
</div>
<!-- ...and so on -->
</sly>
So the point is I don't want to look at someUse.data getting. Instead of it I would do something like this:
<sly data-sly-use.someUse="com.mycompany.SomeUse" data-sly-use.data=${someUse.data}>
${data.firstProperty}
<div>
${data.secondProperty}
</div>
<!-- ...and so on -->
</sly>
I can't do this way though. Is there any alternative to achieve such result? Thanks a lot!
So the answer is to:
<sly data-sly-test.varName="${data.firstProperty}"></sly>
<div> ${varName.secondProperty} </div>
Creating a variable through empty data-sly-test attribute make it accessible after a tag.
You can set variables for use in a WCMUse class with the following syntax:
<sly data-sly-use.someUse="${ 'com.mycompany.SomeUse' # page=currentPage }">
and retrieve the page variable from your WCMUse class's activate() method like this:
Page page = get("page", Page.class);
For a working example, check out this Sightly script and this WCMUse class.
Instead of data-sly-use.data=${someUse.data}
use data-sly-test.data="${someUse.data}".
Then you will be able to get the property like
${data.firstProperty}
<div>
${data.secondProperty}
</div>
You can also set in DOM elements
<div id="elementId" data-sly-test.vehicle="${model.vehicle}">
<p>${vehicle.name}</p>
<p>${vehicle.type}</p>
</div>

Evaluating expression and pass as argument in Sightly AEM

I have the following Sightly expression:
<li data-sly-call="${linkTemplate.dynamicLink # section='education',
url='/en/life-career-events.html', text=${'comp.masthead.navigation.home' # i18n}}">
</li>
The dynamiclink template is as follows:
<div data-sly-template.dynamicLink="${# section, url, text}"
data-sly-use.membersNav="${'com.comp.cms.component.masthead.MembersNavigation' # section=section}">
<a data-sly-attribute.class="${membersNav.cssClass}" href="${url}">${text}</a>
</div>
This doesn't work because text=${'comp.masthead.navigation.home' # i18n} isn't evaluated as a string and then passed into the dynamiclink.
Is this possible? Can I evaluate and assign to a variable or do I have to create a new template when I want to evaluate i18n lookups?
Sightly 1.1 doesn't allow to have expressions within expressions, and there's no plan to change that for now.
Hacking the solution:
There's a trick: data-sly-test can be (ab)used to set variables. It's not really a recommended way to do though, unless you have a real condition, because this will mislead someone who reads the template in thinking that the intention was to have an actual condition.
The trick goes like that: an identifier can be provided to data-sly-test, which will expose the result of the test as a variable. Additionally, data-sly-test will be considered true, unless the resulting string is empty.
For e.g.:
<p data-sly-test.spanishAsset="${'Asset' # i18n, locale='es'}">${spanishAsset}</p>
Outputs:
<p>Recurso</p>
So in your case, you could write:
<li data-sly-test.linkText="${'comp.masthead.navigation.home' # i18n}"
data-sly-call="${linkTemplate.dynamicLink # section='education',
url='/en/life-career-events.html', text=linkText}">
</li>
A cleaner solution
As you probably don't want to explain to all the users of this template that they have to write such a hack, and instead of having two separated templates for translated and non-translated texts, you could instead leverage optional templates parameters. So you might for e.g. have a noI18n optional parameter.
The call would then be as simple as it can be:
<!--/* Translating would be the default behavior */-->
<li data-sly-call="${linkTemplate.dynamicLink #
section='education',
url='/en/life-career-events.html',
text='comp.masthead.navigation.home'}"></li>
<!--/* Or translating could be turned off */-->
<li data-sly-call="${linkTemplate.dynamicLink #
section='education',
url='/en/life-career-events.html',
text='my text...',
noI18n=true}"></li>
The template would then have two data-sly-test conditions for the two cases (note that the data-sly-unwrap attributes can be dropped in AEM 6.1+):
<div data-sly-template.dynamicLink="${# section, url, text, noI18n}"
data-sly-use.membersNav="${'com.comp.cms.component.masthead.MembersNavigation'
# section=section}">
<a href="${url}" data-sly-attribute.class="${membersNav.cssClass}">
<sly data-sly-test="${noI18n}" data-sly-unwrap>${membersNav.text}</sly>
<sly data-sly-test="${!noI18n}" data-sly-unwrap>${membersNav.text # i18n}</sly>
</a>
</div>
Optionally, to keep the template as simple as possible and to remove those conditions, you could also make the Use-API do the translation, depending on the noI18n optional parameter:
<div data-sly-template.dynamicLink="${# section, url, text, noI18n}"
data-sly-use.membersNav="${'com.comp.cms.component.masthead.MembersNavigation'
# section=section, noI18n=noI18n}">
<a href="${url}" data-sly-attribute.class="${membersNav.cssClass}">
${membersNav.text}
</a>
</div>
The proper code for the logic to translate a string is:
Locale pageLang = currentPage.getLanguage(false);
I18n i18n = new I18n(slingRequest.getResourceBundle(pageLang));
String text = i18n.get("Enter a search keyword");