Goal
Before trying out this extension, check if the AEM Core components Carousel serves your purpose - http://opensource.adobe.com/aem-core-wcm-components/library/carousel.html or using a Pathbrowser is good enough - /libs/granite/ui/components/coral/foundation/form/pathbrowser
Create a Touch UI Composite Multifield configuration supporting Images, widgets of type /libs/cq/gui/components/authoring/dialog/fileupload
For AEM 62 check this post
Demo | Package Install | Github
Component Rendering
Image Multifield Structure
Nodes in CRX
Dialog
Dialog XML
<?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="64 Image Multifield"
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">
<products
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/multifield"
composite="{Boolean}true"
fieldLabel="Products">
<field
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container"
name="./products">
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<product
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldDescription="Name of Product"
fieldLabel="Product Name"
name="./product"/>
<path
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathbrowser"
fieldDescription="Select Path"
fieldLabel="Path"
name="./path"
rootPath="/content"/>
<file
jcr:primaryType="nt:unstructured"
sling:resourceType="cq/gui/components/authoring/dialog/fileupload"
allowUpload="false"
autoStart="{Boolean}false"
class="cq-droptarget"
fileNameParameter="./fileName"
fileReferenceParameter="./fileReference"
mimeTypes="[image/gif,image/jpeg,image/png,image/webp,image/tiff]"
multiple="{Boolean}false"
name="./file"
title="Upload Image Asset"
uploadUrl="${suffix.path}"
useHTML5="{Boolean}true"/>
</items>
</column>
</items>
</field>
</products>
</items>
</column>
</items>
</content>
</jcr:root>
Solution
1) Login to CRXDE Lite, create folder (nt:folder) /apps/eaem-touchui-image-multifield
2) Create clientlib (type cq:ClientLibraryFolder) /apps/eaem-touchui-image-multifield/clientlib and set a property categories of String type to cq.authoring.dialog.all, dependencies of type String[] with value underscore
3) Create file ( type nt:file ) /apps/eaem-touchui-image-multifield/clientlib/js.txt, add the following
image-multifield.js
4) Create file ( type nt:file ) /apps/eaem-touchui-image-multifield/clientlib/image-multifield.js, add the following code
(function ($, $document) {
var COMPOSITE_MULTIFIELD_SELECTOR = "coral-multifield[data-granite-coral-multifield-composite]",
FILE_REFERENCE_PARAM = "fileReference",
registry = $(window).adaptTo("foundation-registry"),
ALLOWED_MIME_TYPE = "image/jpeg",
adapters = registry.get("foundation.adapters");
var fuAdapter = _.reject(adapters, function(adapter){
return ((adapter.type !== "foundation-field") || (adapter.selector !== "coral-fileupload.cq-FileUpload"));
});
if(_.isEmpty(fuAdapter)){
return;
}
fuAdapter = fuAdapter[0];
var orignFn = fuAdapter.adapter;
fuAdapter.adapter = function(el) {
return Object.assign(orignFn.call(el), {
getName: function () {
return el.name;
},
setName: function(name) {
var prefix = name.substr(0, name.lastIndexOf(el.name));
el.name = name;
$("input[type='hidden'][data-cq-fileupload-parameter]", el).each(function(i, el) {
if ($(el).data("data-cq-fileupload-parameter") !== "filemovefrom") {
this.setAttribute("name", prefix + this.getAttribute("name"));
}
});
}
});
};
$document.on("foundation-contentloaded", function(e) {
var composites = $(COMPOSITE_MULTIFIELD_SELECTOR, e.target);
composites.each(function() {
Coral.commons.ready(this, function(el) {
addThumbnails(el);
});
});
});
function addThumbnails(multifield){
var $multifield = $(multifield),
dataPath = $multifield.closest(".cq-dialog").attr("action"),
mfName = $multifield.attr("data-granite-coral-multifield-name");
dataPath = dataPath + "/" + getStringAfterLastSlash(mfName);
$.ajax({
url: dataPath + ".2.json",
cache: false
}).done(handler);
function handler(mfData){
multifield.items.getAll().forEach(function(item, i) {
var $mfItem = $(item),
$fileUpload = $mfItem.find("coral-fileupload");
if(_.isEmpty($fileUpload)){
return;
}
var itemName = getJustItemName($fileUpload.attr("name"));
if(_.isEmpty(mfData[itemName]) || _.isEmpty((mfData[itemName][FILE_REFERENCE_PARAM]))){
return;
}
var imagePath = mfData[itemName][FILE_REFERENCE_PARAM];
$fileUpload.trigger($.Event("assetselected", {
path: imagePath,
group: "",
mimetype: ALLOWED_MIME_TYPE, // workaround to add thumbnail
param: "",
thumbnail: getThumbnailHtml(imagePath)
}));
});
}
function getThumbnailHtml(path){
return "<img class='cq-dd-image' src='" + path + "/_jcr_content/renditions/cq5dam.thumbnail.319.319.png'>";
}
function getJustItemName(itemName){
itemName = itemName.substr(itemName.indexOf(mfName) + mfName.length + 1);
itemName = itemName.substring(0, itemName.indexOf("/"));
return itemName;
}
}
function getStringAfterLastSlash(str){
if(!str || (str.indexOf("/") == -1)){
return "";
}
return str.substr(str.lastIndexOf("/") + 1);
}
}(jQuery, jQuery(document)));
5) To render the composite multifield items eg. to create a Image Gallery component, create a HTL render script /apps/eaem-touchui-image-multifield/sample-image-multifield/sample-image-multifield.html
<div>
<b>6420 Composite Image Multifield</b>
<div data-sly-use.company="helper.js" data-sly-unwrap>
<div data-sly-test="${!company.products && wcmmode.edit}">
Add products using component dialog
</div>
<div data-sly-test="${company.products}">
<div data-sly-list.product="${company.products}">
<div>
<div>${product.name}</div>
<div>${product.path}</div>
<div><img src="${product.fileReference}" width="150" height="150"/></div>
</div>
</div>
</div>
</div>
</div>
6) Finally a HTL use-script to read the multifield data /apps/eaem-touchui-image-multifield/sample-image-multifield/helper.js
"use strict";
use( ["/libs/wcm/foundation/components/utils/ResourceUtils.js","/libs/sightly/js/3rd-party/q.js" ], function(ResourceUtils, Q){
var prodPromise = Q.defer(), company = {},
productsPath = granite.resource.path + "/products";
company.products = undefined;
ResourceUtils.getResource(productsPath)
.then(function (prodParent) {
return prodParent.getChildren();
})
.then(function(products) {
addProduct(products, 0);
});
function addProduct(products, currIndex){
if(!company.products){
company.products = [];
}
if (currIndex >= products.length) {
prodPromise.resolve(company);
return;
}
var productRes = products[currIndex],
properties = productRes.properties;
var product = {
path: properties.path,
name: properties.product,
fileReference: properties.fileReference
};
company.products.push(product);
addProduct(products, (currIndex + 1));
}
return prodPromise.promise;
} );