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;
} );




Hi Sreekanth Choudry,
ReplyDeleteDo you have a similar fix for 6.4.2 version? I tried the above provided fix on 6.4.2 but as you mentioned it is working only on 6.4.4.
If you experience order problem like me, you can user below code part:
ReplyDeletesetName: function(name) {
var prefix = name.substr(0, name.lastIndexOf(el.name));
var oldRootElementName = "";
var newRootElementName = "";
if(prefix == ""){
oldRootElementName = el.name.substr(0, el.name.lastIndexOf("./icon"));
newRootElementName = name.substr(0, name.lastIndexOf("./icon"));;
}
el.name = name;
$("input[type='hidden'][data-cq-fileupload-parameter]", el).each(function(i, el) {
if ($(el).data("data-cq-fileupload-parameter") !== "filemovefrom") {
if(prefix == ""){
var currentChildName = this.getAttribute("name");
currentChildName = currentChildName.replace(oldRootElementName,newRootElementName);
this.setAttribute("name", currentChildName);
}else{
this.setAttribute("name", prefix + this.getAttribute("name"));
}
}
});
}
Hi Emrah,
DeleteI replaced the setName function to fix the reorder issue but its not working. Can you please suggest?
For AEM 6.5, the code has to be modified a bit. Please use the following code if working on AEM 6.5
ReplyDeletesetName function to be replaced by the following code. Rest all remains as given in the article above.
setName: function(name) {
var prefix = name.substr(0, name.lastIndexOf(el.name));
var oldRootElementName = "";
var newRootElementName = "";
if(prefix == ""){
oldRootElementName = el.name.substr(0, el.name.lastIndexOf("./"));
newRootElementName = name.substr(0, name.lastIndexOf("./"));
}
el.name = name;
$("input[type='hidden'][data-cq-fileupload-parameter]", el).each(function(i, el) {
if ($(el).data("data-cq-fileupload-parameter") !== "filemovefrom") {
if(prefix == ""){
var currentChildName = this.getAttribute("name");
currentChildName = currentChildName.replace(oldRootElementName,newRootElementName);
this.setAttribute("name", currentChildName);
} else{
this.setAttribute("name", prefix + this.getAttribute("name"));
}
}
});
}
Hi Sreekanth Choudry Nalabotu
ReplyDeleteCan you provide solution for Image Upload feature in multifield in AEM 6.5