I managed to display the anchor link option in the RTE for the text component for authoring. Because on our website we have a fixed header, it's offsetting the anchor link.
I could resolve the issue with CSS but supporting that I'd need a CSS class on the anchor links. Could someone advise how to add a 'link-anchor' class to the anchor links in AEM?
<links jcr:primaryType="nt:unstructured" features="[modifylink,unlink,anchor]" />
<uiSettings jcr:primaryType="nt:unstructured">
<cui jcr:primaryType="nt:unstructured">
<inline jcr:primaryType="nt:unstructured" toolbar="[undo#undo,undo#redo,#paraformat,#styles,-,#format,experience-aem#colorPicker,-,#justify,-,#lists,-,subsuperscript#subscript,subsuperscript#superscript,links#modifylink,links#unlink,links#anchor,edit#cut,edit#copy,edit#paste-plaintext,edit#paste-wordhtml,misctools#specialchars,misctools#sourceedit,-,table#table]">
<popovers jcr:primaryType="nt:unstructured">
<format
jcr:primaryType="nt:unstructured"
items="[format#bold,format#italic,format#underline]"
ref="format"/>
<justify
jcr:primaryType="nt:unstructured"
items="[justify#justifyleft,justify#justifycenter,justify#justifyright]"
ref="justify"/>
<lists
jcr:primaryType="nt:unstructured"
items="[lists#unordered,lists#ordered,lists#outdent,lists#indent]"
ref="lists"/>
<styles
jcr:primaryType="nt:unstructured"
items="styles:getStyles:styles-pulldown"
ref="styles"/>
<paraformat
jcr:primaryType="nt:unstructured"
items="paraformat:getFormats:paraformat-pulldown"
ref="paraformat"/>
</popovers>
</inline>
<dialogFullScreen jcr:primaryType="nt:unstructured" toolbar="[undo#undo,undo#redo,#paraformat,-,#format,experience-aem#colorPicker,-,#justify,-,#lists,-,subsuperscript#subscript,subsuperscript#superscript,links#modifylink,links#unlink,links#anchor,edit#cut,edit#copy,edit#paste-plaintext,edit#paste-wordhtml,misctools#specialchars,misctools#sourceedit,-,table#table]">
<popovers jcr:primaryType="nt:unstructured">
<format
jcr:primaryType="nt:unstructured"
items="[format#bold,format#italic,format#underline]"
ref="format"/>
<justify
jcr:primaryType="nt:unstructured"
items="[justify#justifyleft,justify#justifycenter,justify#justifyright]"
ref="justify"/>
<lists
jcr:primaryType="nt:unstructured"
items="[lists#unordered,lists#ordered,lists#outdent,lists#indent]"
ref="lists"/>
<styles
jcr:primaryType="nt:unstructured"
items="styles:getStyles:styles-pulldown"
ref="styles"/>
<paraformat
jcr:primaryType="nt:unstructured"
items="paraformat:getFormats:paraformat-pulldown"
ref="paraformat"/>
</popovers>
</dialogFullScreen>
<tableEditOptions
jcr:primaryType="nt:unstructured"
toolbar="[table#insertcolumn-before,table#insertcolumn-after,table#removecolumn,-,table#insertrow-before,table#insertrow-after,table#removerow,-,table#mergecells-right,table#mergecells-down,table#mergecells,table#splitcell-horizontal,table#splitcell-vertical,-,table#selectrow,table#selectcolumn,-,table#ensureparagraph,-,table#modifytableandcell,table#removetable,-,undo#undo,undo#redo,-,table#exitTableEditing,-]"/>
</cui>
</uiSettings>
Your usecase is a simplified version of this use case - http://experience-aem.blogspot.com/2017/09/aem-63-touch-ui-extend-rich-text-link-dialog-add-rel-select.html.
Instead of adding drop down and 2 way mapping, you just need to hard code class name during save. This worked for me:
Create a clientlib - /apps/myproj/clientlibs/authoring
Add categories cq.authoring.dialog
Add a new js file as rte-link-class.js. Any name and give same name inside js.txt
Add below code in the rte-link-class.js
(function ($) {
"use strict";
var _ = window._,
Class = window.Class,
CUI = window.CUI,
RTE_LINK_DIALOG = "rtelinkdialog";
if (CUI.rte.ui.cui.CuiDialogHelper.eaemExtended) {
return;
}
var EAEMLinkBaseDialog = new Class({
extend: CUI.rte.ui.cui.CQLinkBaseDialog,
toString: "EAEMLinkBaseDialog",
initialize: function (config) {
this.superClass.initialize.call(this, config);
},
dlgToModel: function () {
this.superClass.dlgToModel.call(this);
this.objToEdit.attributes["class"] = "custom-anchor-link";
},
dlgFromModel: function () {
this.superClass.dlgFromModel.call(this);
},
});
CUI.rte.ui.cui.CuiDialogHelper = new Class({
extend: CUI.rte.ui.cui.CuiDialogHelper,
toString: "EAEMCuiDialogHelper",
instantiateDialog: function (dialogConfig) {
var type = dialogConfig.type;
if (type !== RTE_LINK_DIALOG) {
this.superClass.instantiateDialog.call(this, dialogConfig);
return;
}
var $editable = $(this.editorKernel.getEditContext().root),
$container = CUI.rte.UIUtils.getUIContainer($editable),
dialog = new EAEMLinkBaseDialog();
dialog.attach(dialogConfig, $container, this.editorKernel);
return dialog;
},
});
CUI.rte.ui.cui.CuiDialogHelper.eaemExtended = true;
})(jQuery);
After adding link from RTE, it gets saved in jcr like this
I have a JSON model, which I build from a Metadata set.
So I created that JSON array and did the following:
var oModel = new JSONModel({
JSONDataSet: oJSONDataArray
});
this._oFragment.setModel(oModel);
In my fragment, I have a table:
<Table id="tableId" items="{ path:'/JSONDataSet' }">
<columns>
<Column>
<Text text="HeaderColumn1"/>
</Column>
<!-- ... -->
</columns>
<ColumnListItem>
<Text text="{Value1}"/>
<!-- ... -->
</ColumnListItem>
</Table>
Now everything works fine on my fragment. In my list, I'll see all that data from my JSON model, but I still receive this weird error in my console:
List Binding is not bound against a list for /JSONDataSet
How can I solve this issue?
List Binding is not bound against a list for ...
The above error occurs only in ODataListBinding.js and is thrown when the module fails to find the entityset name within the service $metadata document or if the resulting multiplicity is not "*". source
In your case, the framework assumes that JSONDataSet is some entity set name defined in the $metadata which obviously cannot be found. In order to prevent framework to search for that in $metadata, you'll need to tell that JSONDataSet is not from the unnamed default model (ODataModel) but from another model (JSONModel).
Try to give it a name, and assign the name in the binding definitions like this:
const oModel = new JSONModel({
JSONDataSet: /*some data*/
});
this._oFragment.setModel(oModel, "anotherModel");
<Table id="tableId" items="{anotherModel>/JSONDataSet}">
<!-- ... -->
<ColumnListItem>
<Text text="{anotherModel>Value1}"/>
<!-- ... -->
</ColumnListItem>
</Table>
The framework won't try to resolve anotherModel>/JSONDataSet until that model is registered and set to the fragment. The error will be gone since the framework now knows that it's not initializing ODataListBinding but a client ListBinding.
If you take a look at the browser console, probably you have already an error telling you that "the template or factory function was not provided" or something similar.
In the following code, there is something missing
<Table id="tableId" items="{ path:'/JSONDataSet' }">
<columns>
.....
<columns>
</Table>
if you do items="{ path:'/JSONDataSet' }", it means that you want the items in your list to be created dynamically based on the path /JSONDataSet from your model. This path should point to an array of some kind (usually an array of objects). Using UI5 terms, you are trying to use an aggregation binding.
However, how do you want the items in your Table to be created?
That's why you need to provide a template item, declaring an example item inside your table:
<Table id="tableId" items="{ path:'/JSONDataSet' }">
<columns>
.....
<columns>
<items>
<ColumnListItem>
<cells>
<ObjectIdentifier
title="{a}"
text="{b}"/>
<Text
text="{c}" />
</cells>
</ColumnListItem>
</items>
</Table>
See more examples in the UI5 documentation.
In the code above, a, b and c are proprierties found in every object inside you array.
In the end, if you array contains 10 items, 10 rows will be created in your table. If you want to create columns dynamically, just provide a single Column example and use columns="{ path:'/JSONDataSet'} instead.
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.
I want to build a component in which user can drag & drop two images from content finder. For this i created another tab in my dialog but when i am authoring the component its showing the 1st tab image in both div's. Can you please tell me where i'm wrong here is my code.
dialog.xml
<?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"
activeTab="{Long}0"
helpPath="en/cq/current/wcm/default_components.html#Image"
xtype="tabpanel">
<items jcr:primaryType="cq:WidgetCollection">
<image
jcr:primaryType="cq:Widget"
cropParameter="./imageCrop"
ddGroups="[media]"
fileNameParameter="./fileName"
fileReferenceParameter="./fileReference"
mapParameter="./imageMap"
name="./file"
requestSuffix=".img.png"
rotateParameter="./imageRotate"
title="Image"
xtype="html5smartimage"/>
<advanced
jcr:primaryType="cq:Widget"
title="Advanced"
xtype="panel">
<items jcr:primaryType="cq:WidgetCollection">
<title
jcr:primaryType="cq:Widget"
fieldLabel="Title"
name="./jcr:title"
xtype="textfield"/>
<alt
jcr:primaryType="cq:Widget"
fieldDescription="(leave empty to use the title defined above)"
fieldLabel="Alt Text"
name="./alt"
xtype="textfield"/>
<linkURL
jcr:primaryType="cq:Widget"
fieldDescription="Drop files or pages from the Content Finder"
fieldLabel="Link to"
name="./linkURL"
xtype="pathfield"/>
<description
jcr:primaryType="cq:Widget"
fieldLabel="Description"
name="./jcr:description"
xtype="textarea"/>
<size
jcr:primaryType="cq:Widget"
fieldLabel="Size"
heightParameter="./height"
widthParameter="./width"
xtype="sizefield"/>
</items>
</advanced>
<image4
jcr:primaryType="cq:Widget"
cropParameter="./image4Crop"
ddGroups="[media]"
fileNameParameter="./image4/fileName"
fileReferenceParameter="./image4/fileReference"
mapParameter="./imageMap"
name="./image4/file"
requestSuffix=".img.png"
rotateParameter="./image4/imageRotate"
title="Image"
xtype="html5smartimage"/>
</items>
</jcr:root>
image.jsp is
<%# page import="com.day.cq.commons.Doctype,
com.day.cq.wcm.api.components.DropTarget,
com.day.cq.wcm.foundation.Image" %><%
%><%#include file="/libs/foundation/global.jsp"%><%
Image image = new Image(resource);
//drop target css class = dd prefix + name of the drop target in the edit config
image.addCssClass(DropTarget.CSS_CLASS_PREFIX + "image");
image.loadStyleData(currentStyle);
image.setSelector(".img"); // use image script
image.setDoctype(Doctype.fromRequest(request));
Image image4 = new Image(resource);
//drop target css class = dd prefix + name of the drop target in the edit config
image4.addCssClass(DropTarget.CSS_CLASS_PREFIX + "image4");
image4.loadStyleData(currentStyle);
image4.setSelector(".img"); // use image script
image4.setDoctype(Doctype.fromRequest(request));
// add design information if not default (i.e. for reference paras)
if (!currentDesign.equals(resourceDesign)) {
image.setSuffix(currentDesign.getId());
}
String divId = "cq-image-jsp-" + resource.getPath();
%><div id="<%= divId %>"><% image.draw(out); %></div><%
%><cq:text property="jcr:description" placeholder="" tagName="small" escapeXml="true"/>
<div id="<%= divId %>"><% image4.draw(out); %></div>
<%#include file="/libs/foundation/components/image/tracking-js.jsp"%>
I ran into a problem like this using with multiple images within the dialog. You might check the resourceType after you save the images in CRXDE to make sure they both have a resourceType property set. If either images are missing this property you can add the property using a hidden widget. Reference https://forums.adobe.com/message/4623838 for more
<resType1
jcr:primaryType="cq:Widget"
ignoreData="{Boolean}true"
name="./file/sling:resourceType"
value="foundation/components/image"
xtype="hidden"/>
<resType2
jcr:primaryType="cq:Widget"
ignoreData="{Boolean}true"
name="./image4/sling:resourceType"
value="foundation/components/image"
xtype="hidden"/>
Suppose I have the following XML view:
<mvc:View xmlns:mvc="sap.ui.core.mvc" ...>
<Page>
<content>
<l:VerticalLayout>
<l:content>
<core:Fragment fragmentName="my.static.Fragment" type="XML" />
</l:content>
</l:VerticalLayout>
</content>
</Page>
</mvc:View>
The fragment my.Fragment is statically loaded. However, I now want to dynamically change the to-be-loaded fragment (ideally using data binding the fragmentName property, but any other means should be ok as well), ie. something like this:
<mvc:View xmlns:core="sap.ui.core.mvc" ...>
<Page>
<content>
<l:VerticalLayout>
<l:content>
<core:Fragment fragmentName="{/myDynamicFragment}" type="XML" />
</l:content>
</l:VerticalLayout>
</content>
</Page>
</mvc:View>
However, the latter does not work, and the Fragment definitions don't allow for data binding... I might have missed something, but how should I dynamically change the Fragment in my XML view based on a parameter/model property/etc?
For now, I have resorted to a custom control instead of directly using a fragment in my view, and have that control do the dispatching to the appropriate Fragment, but I feel there should be an easier, out-of-the-box way...
I think the only solution will be initialization of fragment from onInit method of controller:
sap.ui.controller("my.controller", {
onInit : function(){
var oLayout = this.getView().byId('mainLayout'), //don't forget to set id for a VerticalLayout
oFragment = sap.ui.xmlfragment(this.fragmentName.bind(this));
oLayout.addContent(oFragment);
},
fragmentName : function(){
return "my.fragment";
}
});
The fragment name can also result from a binding, including an expression binding which evaluates to a constant. As formatter functions return strings, and not booleans, === 'true' has been added in the following example:
Example: Dynamic Fragment Name
<core:Fragment fragmentName="{= ${path: 'facet>Target', formatter: 'sap.ui.model.odata.AnnotationHelper.isMultiple'} === 'true'
? 'sap.ui.core.sample.ViewTemplate.scenario.TableFacet'
: 'sap.ui.core.sample.ViewTemplate.scenario.FormFacet' }" type="XML"/>