Evaluating expression and pass as argument in Sightly AEM - 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");

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

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

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>.

In TYPO3 Fluid, how do I access a variable from inside a foreach loop?

I am building a navigation with the VHS Menu ViewHelper. Below is the entire code.
My question is probably very basic fluid though.
Inside a scope, how do I access a variable that exists outside that scope - or how do I pass it into that scope?
EDIT: I've updated my code to not use dashes in variable names as well as assigned different names to v:page.menu with as="menuLevel0".
The full code:
{namespace v=Tx_Vhs_ViewHelpers}
<ul id="nav" class="grid-full classic">
<v:page.menu as="menuLevel0" expandAll="1" useShortcutData="1" levels="4" classHasSubpages="parent" classFirst="first" classLast="last">
<f:for each="{menuLevel0}" as="itemLevel0" iteration="iterLevel0">
<li class="{itemLevel0.class} level0 nav-{iterLevel0.cycle} level-top">
<a href='{itemLevel0.link}' class='level-top'><span>
<f:if condition="{itemLevel0.nav_title}"><f:then>{itemLevel0.nav_title}</f:then><f:else>{itemLevel0.title}</f:else></f:if>
nav-{iterLevel0.cycle}</span>
</a>
<f:if condition="{itemLevel0.hasSubPages}">
<f:then>
<v:page.menu as="menuLevel1" expandAll="1" classHasSubpages="parent" classFirst="first" classLast="last">
<ul class="level0">
<f:for each="{menuLevel1}" as="itemLevel1" iteration="iterLevel1">
<li class="{itemLevel1.class} level1 nav-{iterLevel0.cycle}-{iterLevel1.cycle}">
<a href='{itemLevel1.link}' class='level-top'><span>
<f:if condition="{itemLevel1.nav_title}"><f:then>{itemLevel1.nav_title}</f:then><f:else>{itemLevel1.title}</f:else></f:if>
nav-{menuLevel0.iterLevel0.cycle}-{iterLevel1.cycle}
</span></a>
</li>
</f:for>
</ul>
</v:page.menu>
</f:then>
</f:if>
</li>
</f:for>
</v:page.menu>
</ul>
In fluid, variables are and should be scoped to the blocks you are using.
While you are using our v:page.menu ViewHelpers, you need to understand the scoping a bit further:
Use different names for variables in nested scopes: {levelOneIterator} and {levelTwoIterator} are good examples for such behaviour
As a rule of thumb: Use lowerCamelCased variable names. It prevents fluid from parsing your var-names wrong.
The reason why your code is not working:
You assign a {iter} variable. Therefore fluid doesn't parse the dash-ified variable name as it detects the undashified identifier first. Dashes have special meaning in fluid and you can run into kind-of race conditions there.
To take this a bit further:
```
{foo-bar} // echos "baz"
{foo-bar} // echos "bing", as the bar vaiable is read first, and "foo-" isnt a variable in the current scope
```
Update:
Accessing variables from a parent scope doesnt require prefixing your var-call. In a child for-loop, you can access the parent iteration by simply calling the var.
<f:for each="{iterable}" as="iterableItem iteration="iteratorRoot">
{iteratorRoot.index} <!-- Echoes the current, zero-indexed iteration -->
<f:for each="{iterableItem}" as="subItem" iteration="iterationChild">
<!-- STILL echoes the current, zero-indexed iteration -->
{iteratorRoot.index}
<!-- Echoes the current, zero-indexed sub iteration -->
{iterationChild.index}
</f:for>
</f:for>
As long as you give different names to the iterators, you can access them in any scope by their originating name. Same goes for the aliased (as="name") named variables.
Try not using "-" in the variable-name! Use CamelCase instead:
<f:render section="layer2" arguments="{uid: curPage.uid, parentIter: iter.cycle}" />
In Controller
public function listAction() {
$events = $this->eventsRepository->getEventsList();
$this->view->assign('eventsList', $events);
}
In View File you can access like this.
<f:for each="{eventsList}" as="events">
<tr>
<td> {events.event_id} </td>
<td> <f:link.external uri="{events.event_link}" target="_blank">{events.event_link}</f:link.external></td>
<td><f:format.date format="d-m-Y">{events.event_date}</f:format.date></td>
</tr>
</f:for>
Have you tried not to use the same varibale name for the iterators in both of your foreach loops?