Nirmal Jose
Nirmal Jose

Reputation: 118

AEM CQ5 - Multifield custom xtype inside a mutlifield custom xtype

I am trying to create a component in AEM CQ5 which will generate a quiz module. My requirement is to create a dialog which will allow me to create multiple questions each with multiple answers. My dialog xml is as follows -

<?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" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
    jcr:primaryType="cq:Dialog"
    xtype="dialog">
    <items jcr:primaryType="cq:Widget"
        xtype="tabpanel">
        <items jcr:primaryType="cq:WidgetCollection">
            <categories 
                jcr:primaryType="cq:Panel"
                title="Questions & Answers">
                <items jcr:primaryType="cq:WidgetCollection">
                    <questions-answers
                        jcr:primaryType="cq:Widget"
                        title="Questions & Answers"
                        name="./qasegment"
                        xtype="dialogfieldset">
                            <items jcr:primaryType="cq:WidgetCollection">
                                <link
                                    jcr:primaryType="cq:Widget"
                                    fieldDescription="Click on Add item to add questions. Once question is added, click on Edit Answers to add answers and the destination URLs"
                                    name="./questionsAnswers"
                                    typeHint="String"
                                    xtype="multifield">                         
                                    <fieldConfig 
                                        jcr:primaryType="nt:unstructured"
                                        xtype="widgets.configurableQandAfield"/>
                                </link>
                            </items>
                    </questions-answers>
                </items>       
            </categories>
        </items>
    </items>
</jcr:root>

From and xtype:mutlifield, I am referring to a custom extjs widget which is as follows. This will create the (question + question configurations + Add answer multifield ) multifield.

NirmalWeb.ConfigurableQandAField = CQ.Ext.extend(CQ.form.CompositeField, {

     /**
     * @private
     * @type CQ.Ext.form.HiddenField
     */
    hiddenField: null,

    /**
     * @private
     * @type CQ.Ext.form.TextField
     */
    questionField: null,

    /**
     * @private
     * @type CQ.Ext.form.TextField
     */
    linkField: null,

    textField:null,

    answerOptions:null,

    answerType:null,

    widgetConfig: null,


    constructor: function(config) {
        config = config || { };
        var defaults = {
            "border": true,
            "layout": "form",
            "labelSeparator": ":",
            "padding": "10px"
        };
        this.widgetConfig = config;
        config = CQ.Util.applyDefaults(config, defaults);
        EeWeb.ConfigurableQandAField.superclass.constructor.call(this, config);
    },

    // overriding CQ.Ext.Component#initComponent
    initComponent: function() {
        EeWeb.ConfigurableQandAField.superclass.initComponent.call(this);

        // Add a hidden field to hold our result to store.
        this.hiddenField = new CQ.Ext.form.Hidden({
            name: this.name
        });
        this.add(this.hiddenField);


        // Add the link text field and label.
        this.questionField = new CQ.Ext.form.TextField({
            fieldLabel: 'Question',
            allowBlank: false,
            emptyText: "Enter the question",
            width: 500,
            listeners: {
                change: {
                    fn:this.updateQuestionStore
                },       
                dialogclose: {
                    scope: this,
                    fn: this.updateHidden
                }
            }
        });
        this.add(this.questionField);

        var fieldsetConfig = CQ.Ext.apply({}, {
           xtype: 'fieldset',
           title: 'Edit Answer',
           // title or checkboxToggle creates fieldset header
           columnWidth: 0.5,
           checkboxToggle: true,
           collapsed: true,
           items :[
               {    
                xtype: "fieldset",
                title: 'Answer configurations',
                autoHeight:true,
                items: [
                        new CQ.Ext.form.RadioGroup({
                                vertical: false,
                                id:"answerType",
                                items: [
                                    {boxLabel: 'Text Answer', name: 'answerType', inputValue: 1, checked:true},
                                    {boxLabel: 'Image Answer', name: 'answerType', inputValue: 2}
                                ]    

                        }),
                        new CQ.Ext.form.RadioGroup({
                                vertical: false,
                                id:"answerOptions",
                                items: [
                                    {boxLabel: 'Single Answer', name: 'answerOptions', inputValue: 1, checked:true},
                                    {boxLabel: 'Multiple Answer', name: 'answerOptions', inputValue: 2}
                                ]    

                        }),
                       ]
                },
                {
                xtype: "multifield",
                    fieldConfig : {
                        items: [
                            {
                                xtype: "widgets.configurablemultiAnswerfield",
                                hideLabel: true,
                            }
                        ]
                    }
                },
           ]
        });
        this.textField = new CQ.Ext.form.FieldSet(fieldsetConfig);
        this.add(this.textField);

    },

    // overriding CQ.form.CompositeField#processInit
    processInit: function (path, record) { 
        this.linkTextField.processInit(path, record);
        this.linkField.processInit(path, record);
    },

    // overriding CQ.form.CompositeField#setValue
    setValue: function(value) {
        var link = JSON.parse(value);
        this.linkTextField.setValue(link.text);
        this.linkField.show();
        this.hiddenField.setValue(value);
    },

    // overriding CQ.form.CompositeField#getValue
    getValue: function() {
        return this.getRawValue();
    },

    // overriding CQ.form.CompositeField#getRawValue
    getRawValue: function() {
        return JSON.stringify("test");
    },

    // private
    updateHidden: function() {
        this.hiddenField.setValue(this.getValue());
    },

    editAnswer: function(value) {
                alert("radio group change");
     }

});

// Register our new xtype.
CQ.Ext.reg('widgets.configurableQandAfield', NirmalWeb.ConfigurableQandAField);

In the custom widget, I am creating another multifield for adding answer text and tool tip which is another custom widget. Unfortunately, the multifield Add item just adds a text field instead of all the fields in the custom widget 'configurablemultiAnswerfield'.

Please help me out. Let me know if anything more is required. Thanks in advance.

Upvotes: 3

Views: 8973

Answers (2)

Nirmal Jose
Nirmal Jose

Reputation: 118

Thanks all for the answers and suggestions. I was able to fix the issue with the below piece of code. Its little ugly, but it does the job.

  1. The solution to call up the second xtype was easy. I was calling it wrong. It should be just the fieldConfig : {xtype: "widgets.configurableAnswer"} Just to fill up for future reference, I encountered one more issue - to get the data from the second xtype and send it back as JSON.I modified the second xtype updateHidden method as below

     updateHidden: function() {
        this.hiddenField.setValue(this.getValue());
       this.findParentByType('widgets.configurableQandAfield').callUpdate();
    

    }

Call update method in the parent xtype is as below:

    callUpdate: function() {
    this.hiddenField.setValue(this.getValue());
},

To save the data of second xtype from first xtype I used the below code in the getRaw value method

    getRawValue: function() {
    var link = {
        "questionText": this.questionText.getValue(),
        "answerStack" : this.answerSet.findByType('multifield')[0].getValue(),
        };
    return JSON.stringify(link);
},

Attaching the first xtype JS for further reference.

Nirmalweb.ConfigurableQandAField = CQ.Ext.extend(CQ.form.CompositeField, {

 /**
 * @private
 * @type CQ.Ext.form.HiddenField
 */
hiddenField: null,

/**
 * @private
 * @type CQ.Ext.form.TextField
 */
questionText: null,

/**
 * @private
 * @type CQ.Ext.form.FieldSet
 */
answerSet:null,

constructor: function(config) {
    config = config || { };
    var defaults = {
        "border": true,
        "layout": "form",
        "labelSeparator": ":",
        "padding": "10px"
    };
    config = CQ.Util.applyDefaults(config, defaults);
    Nirmalweb.ConfigurableQandAField.superclass.constructor.call(this, config);
},

// overriding CQ.Ext.Component#initComponent
initComponent: function() {
    Nirmalweb.ConfigurableQandAField.superclass.initComponent.call(this);

    // Add a hidden field to hold our result to store.
    this.hiddenField = new CQ.Ext.form.Hidden({
        name: this.name
    });
    this.add(this.hiddenField);

    // Add the link text field and label.
    this.questionText = new CQ.Ext.form.TextField({
        fieldLabel: 'Question',
        cls:"ee-configurableqandafield-text",
        allowBlank: false,
        emptyText: "Enter the question",
        listeners: {
            change: {
                scope:this,
                fn:this.updateHidden,
                fn:this.questionBank
            },       
            dialogclose: {
                scope: this,
                fn: this.updateHidden
            }
        },
        width: 500,
    });
    this.add(this.questionText);

    var fieldsetConfig = CQ.Ext.apply({}, {
        xtype: 'fieldset',
        title: 'Add Answers',
       // title or checkboxToggle creates fieldset header
        columnWidth: 0.5,
        checkboxToggle: true,
        collapsed: true,
        items :[
            {   
            xtype: "fieldset",
            title: 'Answer configurations',
            autoHeight:true,
            items: [
                    {
                        xtype: "selection",
                        type:"select",
                        fieldLabel:'Answer Type',
                        listeners: {
                            change: {
                                scope:this,
                                fn:this.updateHidden,
                            },       
                            dialogclose: {
                                scope: this,
                                fn: this.updateHidden,
                            },
                            selectionchanged :{
                                scope:this,
                                fn:this.updateHiddenAnsType
                            }
                        },
                        options:[
                            {   value:"text",text:"Text Answer"},
                            {   value:"image",text:"Image Answer"}
                        ]
                    },
                    {
                        xtype: "selection",
                        type:"select",
                        fieldLabel:'Answer Options',
                        listeners: {
                            change: {
                                scope:this,
                                fn:this.updateHidden
                            },       
                            dialogclose: {
                                scope: this,
                                fn: this.updateHidden
                            }
                        },
                        options:[
                            {   value:"single",text:"Single Answer"},
                            {   value:"multi",text:"Multiple Answer"}
                        ]
                    },
                   ]
            },
            {
                xtype: "multifield",
                addItemLabel:"Add an answer",
                listeners: {
                    change: {
                        scope:this,
                        fn:this.updateHidden
                    },       
                    dialogclose: {
                        scope: this,
                        fn: this.updateHidden
                    }
                },
                fieldConfig : {
                    xtype: "widgets.configurableAnswer",
                }
            },
       ]
    });
    this.answerSet = new CQ.Ext.form.FieldSet(fieldsetConfig);
    this.add(this.answerSet);


},

// overriding CQ.form.CompositeField#processInit
processInit: function (path, record) { 
    this.questionText.processInit(path, record);
    this.answerSet.processInit(path, record);
},

// overriding CQ.form.CompositeField#setValue
setValue: function(value) {
    var link = JSON.parse(value);
    this.questionText.setValue(link.questionText);
    this.answerSet.setValue(link.answerStack);
    this.hiddenField.setValue(value);
},

// overriding CQ.form.CompositeField#getValue
getValue: function() {
    return this.getRawValue();
},

// overriding CQ.form.CompositeField#getRawValue
getRawValue: function() {
    var link = {
        "questionText": this.questionText.getValue(),
        "answerStack" : this.answerSet.findByType('multifield')[0].getValue(),
        };
    return JSON.stringify(link);
},

// private
updateHidden: function() {
    this.hiddenField.setValue(this.getValue());
},

callUpdate: function() {
    this.hiddenField.setValue(this.getValue());
},

});
// Register our new xtype.
CQ.Ext.reg('widgets.configurableQandAfield', Nirmalweb.ConfigurableQandAField);

the resulting JSON will be like below :

{
"questionText": "What type of user are you?",
"answerStack": [
    "{\"answerText\":\"a1\",\"answerStyle\":\"questionStyle\",\"answerToolTip\":\"t1\",\"destinationType\":\"text\",\"destinationTextCTA\":\"Fill in the Unlock form\",\"destinationTextCTAURL\":\"https://google.com\"}",
    "{\"answerText\":\"a2\",\"answerStyle\":\"questionStyle\",\"answerToolTip\":\"t2\",\"destinationType\":\"text\",\"destinationTextCTA\":\"Fill in the Unlock form 2\",\"destinationTextCTAURL\":\"https://facebook.com\"}"
]

}

Upvotes: 1

Sharath Madappa
Sharath Madappa

Reputation: 3402

I do agree with comments saying it would be better to have a container component instead of complicating the dialog, but if your still looking for a way to pull of the multifield in multifield the following approach should help

You can create the effect using three xtypes :

1) Multicomposite - xtype that stores each multifield entry as a child node with the fields as properties. [Developed by Citytech inc]

2) Multifieldpanel - xtype that stores a multifield as an array of json objects where each field is a property of the json object. [Developed by ACS]

3) Multifield - the out of box multifield xtype.

The dialog structure (using a menu as example) :

multicomposite ( outer multifield ) |-fieldConfigs |- field A (lets say a textfield , for the menu's coulmn name) |- field B (a pathfield to configure the link for the column name ) |- field C (multifield , the inner multifield to hold sub menu entries ) |-fieldConfig - xtype : multifieldpanel |- field Ca (textfield to hold title) |- field Cb (pathfield for the link)

this will store the data in following format :

{
jcr:primaryType: "nt:unstructured",
menu:
    {
        jcr:primaryType: "nt:unstructured",
        item_1:
        {
          submenu:
          [
            "{"title":"Link 1","link":"http://stackoverflow.com"}",
            "{"title":"Link 2","link":"http://stackoverflow.com"}"
          ],
          title: "Main Menu Item 1",
          jcr:primaryType: "nt:unstructured",
          link: "http://stackoverflow.com"
        },
        item_2:
        {
          submenu:
          [
            "{"title":"Sub menu 1","link":"http://stackoverflow.com"}",
            "{"title":"Sub menu 2","link":"http://stackoverflow.com"}"
          ],
          title: "Main Menu item 2",
          jcr:primaryType: "nt:unstructured",
          link: "http://stackoverflow.com"
        }
    }
}

You can find a detailed write up on this method in my post here

Upvotes: 0

Related Questions