AEM 6.3 HTL variable usage - aem

I've really new to AEM, and I'm struggling getting with a button component. It has a drop down asking for open type, so either new window or modal. Ideal the gets target="_blank" or data-modal as part of the render.
Here's my dialog:
<?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="Button"
sling:resourceType="cq/gui/components/authoring/dialog">
<content
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns"
margin="{Boolean}false">
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<label
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldLabel="Button label"
name="./label"/>
<linkTo
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Link to"
name="./linkTo"
rootPath="/content"
suffix=".html"/>
<cssClass
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldLabel="Css class(es)"
name="./cssClass"/>
<open
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/select"
fieldLabel="Open options"
fieldDescription="A new tab/window, or a modal"
name="./open">
<items jcr:primaryType="nt:unstructured">
<def
jcr:primaryType="nt:unstructured"
text="(default)"
value=""/>
<tab
jcr:primaryType="nt:unstructured"
text="New Tab/Window"
value="target='_blank'"/>
<modal
jcr:primaryType="nt:unstructured"
text="Modal Window"
value="data-xxx"/>
</items>
</open>
<secondary
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/checkbox"
checked="${not empty cqDesign.useSecondary ? cqDesign.useSecondary : false}"
fieldDescription="Use the secondary style for the button."
name="./useSecondary"
text="Use secondary style"
uncheckedValue="false"
value="{Boolean}true"/>
</items>
</column>
</items>
</content>
</jcr:root>
and here is my button.java
package apps.bbcom_aem_project.components.content.button;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ValueMap;
import com.adobe.cq.sightly.WCMUsePojo;
public class Button extends WCMUsePojo {
public static final Logger log = LoggerFactory.getLogger(Button.class);
public static final String PROP_LINK_TO = "linkTo";
public static final String PROP_LABEL = "label";
public static final String CSS_CLASS = "cssClass";
public static final String OPEN = "open";
private String linkTo;
private String label;
private String cssClass;
private String open;
#Override
public void activate() throws Exception {
Resource resource = getResource();
ValueMap properties = getProperties();
linkTo = properties.get(PROP_LINK_TO, "#");
label = properties.get(PROP_LABEL, "");
cssClass = properties.get(CSS_CLASS, "");
open = properties.get(OPEN, "");
if (StringUtils.isNotEmpty(linkTo) && !"#".equals(linkTo)) {
// is linkTO does not starts with http
if( !linkTo.startsWith("http") ) {
linkTo = linkTo + ".html";
}
}
log.debug("resource: {}", resource.getPath());
log.debug("linkTo: {}", linkTo);
log.debug("label: {}", label);
}
public String getLinkTo() {
return linkTo;
}
public String getLabel() {
return label;
}
public String getCssClass() {
return cssClass;
}
public String getOpen() {
return open;
}
}
At this point I have no errors, and a maven clean install gives no errors.
Here's my current button.html
<div data-sly-test="${wcmmode.edit || wcmmode.design}"><small class="text-muted"><em>Button Component - Configure</em></small></div>
<a data-sly-use.button="Button" data-sly-test="${button.label != ''}" class="btn ${properties.useSecondary ? 'btn-secondary' : 'btn-primary'} ${button.cssClass}" href="${button.linkTo}" role="button" data-opentype="${button.open}" ${button.open} >${button.label} ${button.open}</a>
and when I inspect the element, I see this:
<a class="btn btn-secondary " href="#" role="button" data-opentype="data-xxx" ${button.open}="">Workspaces data-xxx</a>
The data-xxx matches what I selected in the component options, but I can't get that to render in the opening tag.

HTL (previously known as Sightly) uses HTML5 data attributes to define statements over blocks of markup.
This markup is missing the data attribute, so is not HTML5 compliant
<a ... ${button.open}></a>
You can use the data-sly-attribute statement to set the attribute but you required to pass a key-value pairs map object
<a ... data-sly-attribute="${button.open}"></a>
This will output
<a .... target="_blank"></a>
Also you should consider moving from WCMUsePojo to Sling Models as is recommended by Adobe

The syntax <a ${button.open}=""></a> would not work as it is not part of the HTL spec. If you want to render an attribute name AND value you must use:
<a data-sly-attribute="${button.open}"></a>
Please note that button.open must be an object, preferably a map, for example
{"data-xxx":""}
This will render:
<a data-xxx=""></a>
please refer to the data-sly-attribute spec

Here's my solution:
<div data-sly-test="${wcmmode.edit || wcmmode.design}"><small class="text-muted"><em>Button Component - Configure</em></small></div>
<sly data-sly-test="${properties.open == 'tab'}">
<a data-sly-use.button="Button" data-sly-test="${button.label != ''}" class="btn ${properties.useSecondary ? 'btn-secondary' : 'btn-primary'} ${button.cssClass} tab" href="${button.linkTo}" role="button" target="_blank" > ${button.label} ${button.open} </a>
</sly>
<sly data-sly-test="${properties.open == 'modal'}">
<a data-sly-use.button="Button" data-sly-test="${button.label != ''}" class="btn ${properties.useSecondary ? 'btn-secondary' : 'btn-primary'} ${button.cssClass} modal" href="${button.linkTo}" role="button" data-lity > ${button.label} ${button.open} </a>
</sly>
<sly data-sly-test="${!properties.open}">
<a data-sly-use.button="Button" data-sly-test="${button.label != ''}" class="btn ${properties.useSecondary ? 'btn-secondary' : 'btn-primary'} ${button.cssClass}" href="${button.linkTo}" role="button" > ${button.label} ${properties.open} </a>
</sly>
It's not the cleanest, but as I only have two options, it seems to work.

Related

How to show/hide an element in real time (Blazor)?

I have an image I would like to display only after a user has filled in all text fields.
I have tried using disabled attribute, but that does not seem to work. Any other insights?
Here is my current code:
<EditForm EditContext="#EditContext" style="max-width:800px;" onkeydown="javascript: DisableEnterKey();">
<FluentValidator />
<img src="images/approval-16-grey.ico" alt="Image" disabled="#OkayDisabled">
<p class="statp">How many families and/or individuals are living on your land?</p><br />
<label class="statlabel" for="amountOfFamilies">Amount of families:</label><br />
<InputNumber id="fams" for="indivNum" class="input" #bind-Value="#familyData.IndividualAmount" onwheel="this.blur()" placeholder="Families..." autofocus />
<ValidationMessage For="() => familyData.IndividualAmount" />
<br /><hr class="statHR" />
<label class="statlabel" for="amountOfIndividuals">Amount of single individuals: </label><br />
<InputNumber id="individuals" for="famNum" class="input" #bind-Value="#familyData.FamilyAmount" onwheel="this.blur()" placeholder="Individuals..."/>
<ValidationMessage For="() => familyData.FamilyAmount" />
<br /><hr class="statHR" />
<label class="statlabel" for="names"> Please enter all of the names here:</label><br />
<InputTextArea id="names" class="textArea" rows="4" cols="18" #bind-Value="#PersonName" placeholder="Names of all individuals..." />
<ValidationMessage For="() => familyData.PersonName" />
</EditForm>
</div>
</ul>
#code
{
private EditContext? EditContext;
public FamilyData Model = new FamilyData();
protected string OkayDisabled { get; set; } = "disabled";
protected override void OnInitialized()
{
EditContext = new EditContext(Model);
EditContext.OnFieldChanged += EditContext_OnFieldChanged;
base.OnInitialized();
}
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
SetOkDisabledStatus();
}
private void EditContext_OnFieldChanged(object? sender, FieldChangedEventArgs e)
{
SetOkDisabledStatus();
}
private void SetOkDisabledStatus()
{
if(EditContext.Validate())
{
OkayDisabled = null;
}
else
{
OkayDisabled = "disabled";
}
}
}
The hidden html attribute also works to hide an element.
<p hidden>This paragraph should be hidden.</p>
To bind to Model:
<p hidden="#HideLabel">I am Hidden When HideLabel == true</p>
<p hidden="#(!HideLabel)">I am Hidden when Hidelabel == false</p>
<button #onclick="#Toggle">Show/Hide</button>
#code {
private bool HideLabel {get;set;} = false;
private void Toggle()
{
HideLabel = !HideLabel;
}
}
Edit: You can also use a CSS class to hide/show an element:
<div class="font-italic #(HideLabel ? "d-none" : "d-show")">
I am Hidden When HideLabel == true
</div>
Change OkayDisabled to a bool, and then around your image do this
#if (!OkayDisabled)
{
<img src=".....whatever" etc />
}
You might also want to add #bind:event="oninput" wherever you use an #bind.
Instead of binding your flag to the disabled attribute (an image's disabled attribute just grays it out), I would bind it to a css class that has display: none;
.hidden {
display: none;
}
<img class="#(ShouldShowImage? "hidden" : string.Empty)">
didn't used it within editform but should work
#if(OkayDisabled)
{
<img src="images/approval-16-grey.ico" >

Retrieving values from multifield component in HTL in AEM 6.5

I have a multifield component following this format
<?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="Awards List"
sling:resourceType="cq/gui/components/authoring/dialog">
<content
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/tabs"
type="-nav"/>
<items jcr:primaryType="nt:unstructured">
<awards
jcr:primaryType="nt:unstructured"
jcr:title="Awards Properties"
sling:resourceType="granite/ui/components/foundation/section">
<layout
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">
<description
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
fieldLabel="Description"
name="./description"/>
<awards
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/multifield"
composite="{Boolean}true"
fieldLabel="Awards">
<field
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container"
name="./awards">
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<awardtype
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/select"
fieldDescription="Select Award Type"
fieldLabel="Award Type"
name="./type">
<items jcr:primaryType="nt:unstructured">
<gold
jcr:primaryType="nt:unstructured"
text="gold"
value="gold"/>
<silver
jcr:primaryType="nt:unstructured"
text="silver"
value="silver"/>
<bronze
jcr:primaryType="nt:unstructured"
text="bronze"
value="bronze"/>
<other
jcr:primaryType="nt:unstructured"
text="other"
value="other"/>
</items>
</awardtype>
<award
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldDescription="Name of Award"
fieldLabel="Award Name"
name="./award"/>
</items>
</column>
</items>
</field>
</awards>
</items>
</column>
</items>
</awards>
</items>
</content>
</jcr:root>
And I'm trying to just output the contents of the multifield into a list.
And I'm attempting to do so with
<ul data-sly-list="${properties.awards}">
<li>${item.type}</li>
</ul>
But it doesn't render anything. As a test I did
<ul data-sly-list="${[1,2,3,4]}">
<li>${item}</li>
</ul>
Which did work. Looking online I found resources like https://helpx.adobe.com/experience-manager/using/aem65_coral_resourcetypes.html#UseaDataSourceObjecttopopulateaSelectfield
But they seem to be using Java classes to generate the multifield and I'm hoping that's not necessary. I don't need any extra logic all I'm trying to do is display the values of the fields.
Is there something I'm doing wrong? Does using multifields require making a Java class to handle it?
EDIT: I've tried getting the content using a javascript object by having a js file with the contents
"use strict";
use(function () {
var description = granite.resource.properties["description"];
var awards = granite.resource.properties["awards"];
return {
description: description,
};
});
and using
<div data-sly-use.awardsObject="awardslist.js">
<p>
${awardsObject.description}
${awardsObject.awards}
</p>
</div>
But I can't get awards to return anything. I've tried stringifying the awards object to see if I get any data, but I get none.
It is probably because you are using a composite multifield (look at the property composite="{Boolean}true" against the multifield) which generally handles the form content as composite and creates child nodes under the current component to hold the property values.
Quoting from the docs
true to handle the form content value as composite.
Composite multifield supports nesting another multifield (composite or
not). However, non-composite one doesn’t support nesting.
For example, given the name property of field is addresses, and the
descendant fields have following name property values:
street1
street2
postcode
city/name
city/state
city/country/name
gps/lat
gps/long
it would save the following structure in the repository:
+ addresses + item0
- street1
- street2
- postcode
+ city
- name
- state
+ country
- name
+ gps
- lat
- long + item1
- street1
- street2
- postcode
+ city
- name
- state
+ country
- name
+ gps
- lat
- long
Since the properties object only holds the properties of the current resource, ${properties.awards} would be null and hence it doesn't display anything.
It would be easier to create either a Sling Model or Java / Javascript Use API class to get the list and then use it in the HTL file.
Sample JS Use API
"use strict";
use(function () {
var awards = resource.getChild("awards").listChildren();
return {
awards: awards,
};
});
Sample HTL code
<sly data-sly-use.children="children.js">
<ul data-sly-list.award="${children.awards}">
<li>${award.type}</li>
</ul>
<sly>
Kindly note that the properties object, which is an instance of ValueMap only returns the properties of the current resource. Since the multifield values are stored as child resources, you need to access the child resource first before accessing its properties.

Strange Checkbox behaviour using MVVM in ZK Framework

I've found strange behaviour of zk checkbox in MVVM. I made a MVVM form that shows list of items and detailed view of selected item. I placed checkbox on detailed view and bind it to boolean property of my POJO. And when I select an item with true value of this property checkbox is displayed checked, but next when I select an item with false value of property and then select an item with true value again checkbox is displayed unchecked.
I can illustrate this issue using ZK's MVVM tutorial http://books.zkoss.org/wiki/ZK_Getting_Started/Get_ZK_Up_and_Running_with_MVVM (source code http://sourceforge.net/projects/zkbook/files/GettingStarted/getzkup-20131127.zip/download )
Add to Car class boolean property:
private Boolean cool = false;
public Car(Integer id, String model, String make, String description, String preview, Integer price, boolean cool){
this.id = id;
this.model = model;
this.make = make;
this.preview = preview;
this.description = description;
this.price = price;
this.cool = cool;
}
public Boolean getCool() {
return cool;
}
public void setCool(Boolean cool) {
this.cool = cool;
}
Change CarServiceImpl.java to initialize boolean property for our demo:
carList.add(
new Car(id++,
"Camry",
"Toyota",
"The Toyota Camry is a midsize car ... ",
"/img/car3.png",
24170, true));
carList.add(
new Car(id++,
"Century",
"Toyota",
"The Toyota Century is ... " ,
"/img/car4.png",
28730, true));
Change searchMvvm.zul (add lines labeled as INSERTED):
<window title="Search" width="600px" border="normal" apply="org.zkoss.bind.BindComposer"
viewModel="#id('vm') #init('tutorial.SearchViewModel')">
<hbox align="center">
Keyword:
<textbox value="#bind(vm.keyword)" />
<button label="Search" image="/img/search.png" onClick="#command('search')" />
</hbox>
<listbox height="160px" model="#bind(vm.carList)" emptyMessage="No car found in the result"
selectedItem="#bind(vm.selectedCar)">
<listhead>
<listheader label="Model" />
<listheader label="Make" />
<listheader label="Price" width="20%"/>
<listheader label="Cool" /> <!-- INSERTED -->
</listhead>
<template name="model">
<listitem>
<listcell label="#bind(each.model)"></listcell>
<listcell label="#bind(each.make)"></listcell>
<listcell>$<label value="#bind(each.price)" /></listcell>
<listcell><checkbox checked="#bind(each.cool)" /></listcell> <!-- INSERTED -->
</listitem>
</template>
</listbox>
<hbox style="margin-top:20px">
<image width="250px" src="#bind(vm.selectedCar.preview)" />
<vbox>
<label value="#bind(vm.selectedCar.model)" />
<label value="#bind(vm.selectedCar.make)" />
<label value="#bind(vm.selectedCar.price)" />
<label value="#bind(vm.selectedCar.description)" />
<checkbox checked="#bind(vm.selectedCar.cool)" label="Cool" /> <!-- INSERTED -->
</vbox>
</hbox>
After that start Tomcat and enter localhost:8080/tutorial/searchMvvm.zul in your browser. When you click on Toyota Camry item everything is ok, but when you click on Nissan Cifiro and then on Toyota Camry again checkbox in detaled view will be displayed unchecked. But checkbox in listbox works fine.
Do you know any workaround to solve this problem?
I have the same problem, but it was fixed when I change getCool() with isCool() (on ZK V7)
I'm using ZK7 and I had the same problem.
I got a :
Property 'standard' not readable on type
on the Boolean standard, which has getter isStandard and setter setStandard
The solution is:
When using Boolean (the object) then you have to use get-Prefix
When using boolean (the primitive) then you can use is-Prefix
That's it !
You should use
checkbox.setCheck(Boolean.valueOf(true));

How to control object in multiple zul files

I need to control a gmaps (in gmaps.zul file) from a button in another zul file (top.zul).
Both files are included in root.zul.
When I push my button ZK show me a popup error message: "Name is empty or target is null."
Here's my code:
*root.zul *
<?link rel="stylesheet" type="text/css" href="/resources/css/style.css"?>
<zk>
<borderlayout hflex="1" vflex="1">
<north height="100px" border="none" >
<include id="northPanel" src="/top.zul"></include>
</north>
<center id="mainContent" autoscroll="true">
<include src="/gmaps.zul" ></include>
</center>
</borderlayout>
</zk>
*gmaps.zul *
<zk>
<div>
<gmaps id="map" width="500px" height="500px" lat="35" lng="-110" />
</div>
</zk>
** top.zul **
<zk>
<div apply="test.TestComposer">
<button id="btn" label="add marker" />
</div>
</zk>
** TestComposer.java **
public class TestComposer extends GenericForwardComposer {
private Gmaps map;
private double lat = 35;
private double lng = -110;
public void onClick$btn() {
Events.echoEvent("onAddMarker", map, null);
}
public void onAddMarker$map() {
Gmarker marker = new Gmarker();
lat += 0.001;
lng += 0.001;
marker.setLat(lat);
marker.setLng(lng);
marker.setParent(map);
}
}
You need to wire your map:
#Wire
private Gmaps map;

Hide row in dynamic grid in ZK framework

I am trying to hide/show a row in a grid (filled dynamically from Java code) upon clicking on it's previous row. To simulate a simple MasterDetail component. I get the right index of the clicked row in the java code, however changing visibility of the row doesn't work! Can anyone help me with this or is there a similar way to to this?
Thanks,
Pooya
Here is the code for the ZUL:
<?xml version="1.0" encoding="UTF-8"?>
<zk xmlns="http://www.zkoss.org/2005/zul">
<window id="callbackLogWindow"
apply="CallbackLogWindowComposer"
border="none" height="100%" width="100%"
xmlns:w="http://www.zkoss.org/2005/zk/client">
<grid id="callbackLogGrid" oddRowSclass="non-odd" height="100%">
<columns>
<column label="Logging Name" />
<column label="Status" />
<column label="DateTime" />
<column label="Subject" />
<column label="Replies" />
</columns>
<rows>
<zk forEach="${callbackLogWindow$composer.callbacks}">
<row sclass='${forEachStatus.index % 2 != 0 ? "z-grid-odd" : ""}'
onClick="callbackLogWindow$composer.toggleRow(self.index)">
<custom-attributes callback="${each}"/>
<cell><label value="${callback.loggingName}" /></cell>
<cell><label value="${callback.resolved}" /></cell>
<cell><label value="${callback.callbackTime}" /></cell>
<cell><label value="${callback.subject}" /></cell>
<cell><label value="${callback.resolvedItemCount}" /></cell>
</row>
<row sclass='${forEachStatus.index % 2 != 0 ? "z-grid-odd" : ""}'>
<cell colspan="5">
<include src="callbackItem.zul" callback="${each}"/>
</cell>
</row>
<row>
<custom-attributes callback="${each}"/>
<button onClick="callbackLogWindow$composer.saveCallbackItems(callback)">
Save
</button>
</row>
</zk>
</rows>
</grid>
</window>
</zk>
And the controller:
public class CallbackLogWindowComposer extends SelectorComposer<Window> {
#Inject private CallbackDao callbackDao;
#Wire Grid callbackLogGrid;
private List<Callback> callbacks = new ArrayList<Callback>();
#Override
public void doAfterCompose(Window window) throws Exception {
super.doAfterCompose(window);
}
public List<Callback> getCallbacks() {
callbacks = callbackDao.findAll();
return callbacks;
}
public void toggleRow(int i) {
Component row = callbackLogGrid.getRows().getChildren().get(i+1);
row.setVisible(row.isVisible());
callbackLogGrid.renderAll();
}
public void saveCallbackItems(Callback cb) {
callbackDao.saveInTransaction(cb);
}
}
I am not sure and not test but i can suggest one thing here to you Zk have visible="true/false" attribute you can apply this attribute in each row and bind this with your databean variable For more detail i can suggest let us suppose you have a List<A> list
and list contain all the record which you want to display now add another variable like display in class A and control it by your self .And onClick on any row change update any other item of list .
I have the same problem, and the solution I found was:
row visible=""
<grid model="#bind(vm.total)" vflex="1" emptyMessage="No records exist" width="315px" >
<columns>
<column width="45%"/>
<column width="25%" />
</columns>
<template name="model">
<row visible="#load(each.visible)">
<checkbox label="#load(each.label)" style="font-weight:bold" if="${each.checkbox}"/>
<label value="#load(each.label)" style="font-weight:bold" unless="${each.checkbox}"/>
<doublebox value="#bind(each.value)" sclass="textBoxNumber" locale="us" readonly="true" />
</row>
</template>
</grid>
</groupbox>