AEM 61 - Classic UI Composite Multifield Storing Values as Nodes

Goal


Create a Classic UI Composite Multifield, storing the composite field values as child nodes (useful when executing search queries for exact matches).

For storing values in json format, check this post

For Touch UI Composite Multifield storing values as child nodes check this post

Tested on AEM 61; should work ok on 60 and 561 too...  Demo | Package Install


Bug Fixes

Not working with selection drop downs in multifield - Demo | Package Install


Multi Field Panel



Value Nodes in CRX






Dialog in CRX




Dialog XML

#31, #37, #43 - property dName used in creating fully qualified name (./<multifield-name>/<order>/<dName>) eg ./stock/1/year

<?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"
    title="Multi Field"
    xtype="dialog">
    <items
        jcr:primaryType="cq:Widget"
        xtype="tabpanel">
        <items jcr:primaryType="cq:WidgetCollection">
            <tab1
                jcr:primaryType="cq:Panel"
                title="Add">
                <items jcr:primaryType="cq:WidgetCollection">
                    <stock
                        jcr:primaryType="cq:Widget"
                        hideLabel="false"
                        name="./stock"
                        title="Stock"
                        xtype="multifield">
                        <fieldConfig
                            jcr:primaryType="cq:Widget"
                            border="true"
                            hideLabel="true"
                            layout="form"
                            padding="10px"
                            width="1000"
                            xtype="multi-field-panel">
                            <items jcr:primaryType="cq:WidgetCollection">
                                <product-year-value
                                    jcr:primaryType="cq:Widget"
                                    dName="year"
                                    fieldLabel="Year"
                                    width="60"
                                    xtype="textfield"/>
                                <product-price-value
                                    jcr:primaryType="cq:Widget"
                                    dName="price"
                                    fieldLabel="Price"
                                    width="60"
                                    xtype="textfield"/>
                                <product-version-value
                                    jcr:primaryType="cq:Widget"
                                    dName="version"
                                    fieldLabel="Path to Version"
                                    xtype="pathfield"/>
                            </items>
                        </fieldConfig>
                    </stock>
                </items>
            </tab1>
        </items>
    </items>
</jcr:root>


Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/classic-ui-multi-field-panel-node-store

2) Create clientlib (type cq:ClientLibraryFolder/apps/classic-ui-multi-field-panel-node-store/clientlib and set a property categories of String type to cq.widgets

3) Create file ( type nt:file ) /apps/classic-ui-multi-field-panel-node-store/clientlib/js.txt, add the following

                         multi-field.js

4) Create file ( type nt:file ) /apps/classic-ui-multi-field-panel-node-store/clientlib/multi-field.js, add the following code

CQ.Ext.ns("ExperienceAEM");

ExperienceAEM.MultiFieldPanel = CQ.Ext.extend(CQ.Ext.Panel, {
    constructor: function(config){
        config = config || {};
        ExperienceAEM.MultiFieldPanel.superclass.constructor.call(this, config);
    },

    initComponent: function () {
        ExperienceAEM.MultiFieldPanel.superclass.initComponent.call(this);

        function addName(items, prefix, counter){
            items.each(function(i){
                if(!i.hasOwnProperty("dName")){
                    return;
                }

                i.name = prefix + "/" + (counter) + "/" + i.dName;

                if(i.el && i.el.dom){ //form serialization workaround
                    i.el.dom.name = prefix + "/" + (counter) + "/" + i.dName;
                }
            },this);
        }

        var multi = this.findParentByType("multifield"),
            multiPanels = multi.findByType("multi-field-panel");

        addName(this.items, this.name, multiPanels.length + 1);

        multi.on("removeditem", function(){
            multiPanels = multi.findByType("multi-field-panel");

            for(var x = 1; x <= multiPanels.length; x++){
                addName(multiPanels[x-1].items, multiPanels[x-1].name, x);
            }
        });
    },

    afterRender : function(){
        ExperienceAEM.MultiFieldPanel.superclass.afterRender.call(this);

        this.items.each(function(){
            if(!this.contentBasedOptionsURL
                || this.contentBasedOptionsURL.indexOf(CQ.form.Selection.PATH_PLACEHOLDER) < 0){
                return;
            }

            this.processPath(this.findParentByType('dialog').path);
        })
    },

    getValue: function () {
        var pData = {};

        this.items.each(function(i){
            if(!i.hasOwnProperty("dName")){
                return;
            }

            pData[i.dName] = i.getValue();
        });

        return pData;
    },

    setValue: function (value) {
        var counter = 1, item,
            multi = this.findParentByType("multifield"),
            multiPanels = multi.findByType("multi-field-panel");

        if(multiPanels.length == 1){
            item = value[counter];
        }else{
            item = value;
        }

        this.items.each(function(i){
            if(!i.hasOwnProperty("dName")){
                return;
            }

            i.setValue(item[i.dName]);
        });

        if(multiPanels.length == 1){
            while(true){
                item = value[++counter];

                if(!item){
                    break;
                }

                multi.addItem(item);
            }
        }
    },

    validate: function(){
        return true;
    },

    getName: function(){
        return this.name;
    }
});

CQ.Ext.reg("multi-field-panel", ExperienceAEM.MultiFieldPanel);

5) A sample exact match query performed with query builder, on property name year, returning multifield nodes

http://localhost:4502/bin/querybuilder.json?property=year&property.value=2010&p.hits=full


15 comments:

  1. Hi Sreekanth, I am facing one issue with the Package Install given here. Here is the scenario:
    - I add 1st set of entries on Dialog.
    - I put values in them.
    - I add 2nd set of entries, keep them blank
    - I try to move the 2nd set to the first position
    - The 2nd set moves up, but the values for blank fields get populated from the 1st set.
    - This is happening for the Move Down icon button also.

    Can you please help me with a fix ?

    ReplyDelete
    Replies
    1. Are you sure? this demo is on 61 https://drive.google.com/file/d/0B4d6KmbLkAumV2tmSldNYS1MM1E/view?usp=sharing

      Delete
  2. Hi Sreekanth ,

    If I further needs to store path , price , and value as nodes instead of the properties , how can I achieve this? I have a requirement where in my multifield I have two pathfields ( one for hero image) and one for thumbnail image. Now I want to store each of the properties as a separate node inside node 1 and node 2 so that I can use image servlet to render my dam images .


    1/heroImage/fileReference = path to hero image
    1/heroImage/sling:resourceType = ‘foundation/components/image’
    1/thumbnailImage/fileReference = path to thumbnail image
    1/thumbnailImage/sling:resourceType = ‘foundation/components/image’

    …then paths to the image servlets would be:

    ./1/heroImage.img.png
    ./1/thumbnailImage.img.png

    Please advise. Also , is it really worth to write a custom xtype just to render dam images using image servlet and not using direct dam asset path from path field.

    ReplyDelete
    Replies
    1. Dg, you should check http://experience-aem.blogspot.com/2014/04/aem-cq-56-slide-show-component.html

      Delete
  3. Hi Sreekanth,

    Nodes are not creating for Drop down fields. I need to store the drop down values as well. For example image left align true/false;
    Can you please help me to fix this issue?

    Thanks,
    Syam

    ReplyDelete
    Replies
    1. syam, can you upload the component zip to dropbox/gdrive and share the link here...

      Delete
    2. Sreekanth, sorry for late response. Here is the link https://drive.google.com/file/d/0B5r-Uc8tQnYURTVCY0trT1R0a0k/view?usp=sharing

      Delete
    3. Sreekanth, Please let me is there any fix for it.

      Delete
    4. syam,check the bugfixes section above

      Delete
    5. Thank you very much Sreekanth..

      Delete
  4. Hi Sreekanth, would you be able to help make it support for both xtype=selection, type=radiogroup and xtype=selection, type=checkbox?

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. If you are having issues with the node data displaying in the dialog (after saving and refreshing), try adding a "name" property with "./[same as dName]".
    My data was saved in CRX, but the dialog would just show an empty box for each node. This fixed my issue running 6.2

    ReplyDelete
  7. Hi Sreekanth, What if i need to add fieldset in it and have some properties under it . so it is not saving the data in jcr nodes

    ReplyDelete
  8. This comment has been removed by the author.

    ReplyDelete