Skip navigation

CQ5

Currently Being Moderated

How to build a custom multifield component?

Mar 23, 2012 8:39 AM

Tags: #cq5

How to build a custom multifield component, having a text and a path field in each new entry?

 
Replies
  • Currently Being Moderated
    Mar 25, 2012 12:32 AM   in reply to pulsar440

    To achieve the same, please refer to the steps below:

    Within your app project, define a folder called “clientlib”. Create a text files called “js.txt” and specify the name of the js file which holds the custom xtype definition.

    Example:
    #base=js
    CustomPathField.js

    Define your js file which extends CQ.form.CompositeField and specify attributes required for the custom type. Here is an example for the same:

    /**
    * @class CQ.CustomPathFieldWidget
    * @extends CQ.form.CompositeField
    * This is a custom path field with link text and target
    * @param {Object} config the config object
    */
    /**
    * @class Ejst.CustomWidget
    * @extends CQ.form.CompositeField
    * This is a custom widget based on {@link CQ.form.CompositeField}.
    * @constructor
    * Creates a new CustomWidget.
    * @param {Object} config The config object
    */
    CQ.CustomPathFieldWidget = CQ.Ext.extend(CQ.form.CompositeField, {

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

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

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

    /**
    * @private
    * @type CQ.Ext.form.CheckBox
    */
    openInNewWindow: null,

    /**
    * @private
    * @type CQ.Ext.form.FormPanel
    */
    formPanel: null,

    constructor: function (config) {
    config = config || {};
    var defaults = {
    “border”: true,
    “labelWidth”: 75,
    “layout”: “form”
    //”columns”:6
    };
    config = CQ.Util.applyDefaults(config, defaults);
    CQ.CustomPathFieldWidget.superclass.constructor.call(this, config);
    },

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

    // Hidden field
    this.hiddenField = new CQ.Ext.form.Hidden({
    name: this.name
    });
    this.add(this.hiddenField);

    // Link text
    this.add(new CQ.Ext.form.Label({
    cls: “customwidget-label”,
    text: “Link Text”
    }));
    this.linkText = new CQ.Ext.form.TextField({
    cls: “customwidget-1″,
    fieldLabel: “Link Text: “,
    maxLength: 80,
    maxLengthText: “A maximum of 80 characters is allowed for the Link Text.”,
    allowBlank: true,
    listeners: {
    change: {
    scope: this,
    fn: this.updateHidden
    }
    }
    });
    this.add(this.linkText);

    // Link URL
    this.add(new CQ.Ext.form.Label({
    cls: “customwidget-label”,
    text: “Link URL”
    }));
    this.linkURL = new CQ.form.PathField({
    cls: “customwidget-2″,
    fieldLabel: “Link URL: “,
    allowBlank: false,
    width: 225,
    listeners: {
    change: {
    scope: this,
    fn: this.updateHidden
    },
    dialogclose: {
    scope: this,
    fn: this.updateHidden
    }
    }
    });
    this.add(this.linkURL);

    // Link openInNewWindow
    this.openInNewWindow = new CQ.Ext.form.Checkbox({
    cls: “customwidget-3″,
    boxLabel: “New window”,
    listeners: {
    change: {
    scope: this,
    fn: this.updateHidden
    },
    check: {
    scope: this,
    fn: this.updateHidden
    }
    }
    });
    this.add(this.openInNewWindow);

    },

    processInit: function (path, record) {
    this.linkText.processInit(path, record);
    this.linkURL.processInit(path, record);
    this.openInNewWindow.processInit(path, record);
    },

    setValue: function (value) {
    var link = JSON.parse(value);
    this.linkText.setValue(link.text);
    this.linkURL.setValue(link.url);
    this.openInNewWindow.setValue(link.openInNewWindow);
    this.hiddenField.setValue(value);
    },

    getValue: function () {
    return this.getRawValue();
    },

    getRawValue: function () {
    var link = {
    “url”: this.linkURL.getValue(),
    “text”: this.linkText.getValue(),
    “openInNewWindow”: this.openInNewWindow.getValue()
    };
    return JSON.stringify(link);
    },

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

    1. CQ.Ext.reg(‘mypathfield’, CQ.CustomPathFieldWidget);

     

    So, once we have defined that, it’s time to use in your component Here is snippet from dialog.xml from component:

     

    <linkspanel
    jcr:primaryType="cq:Panel"
    border="false"
    height=""
    title="Links"
    width="">
    < items jcr:primaryType="cq:WidgetCollection">
    < links
    jcr:primaryType="cq:Widget"
    fieldDescription="Press + to add more links"
    fieldLabel="Links"
    hideLabel="true"
    name="./links"
    width="1000"
    xtype="multifield">
    <fieldConfig
    jcr:primaryType="cq:Widget"
    xtype="mypathfield"/>
    < listeners
    jcr:primaryType="nt:unstructured" />
    < /links>
    < /items>
    < /linkspanel>

     
    |
    Mark as:
  • Currently Being Moderated
    Mar 26, 2012 3:49 PM   in reply to Shelly Goel

    Storing the objects as json strings in the multifield array works, but is not so nice, as reading the content (e.g. in the rendering JSP) requires parsing of JSON and searching for individual properties won't work.

     

    In general, for cases of multiple objects (instead of a simple array of individual values, for which the MultiField and the JCR multi-value property is made for), I suggest to use parsys + individual components. See also my reply on the day-communique google group.

     

    Cheers,

    Alex

     
    |
    Mark as:
  • Currently Being Moderated
    Mar 26, 2012 8:24 PM   in reply to aklimets

    Hi Aklimets,

     

    The link you provided dosnt work, can you please check?

     

    Thanks

    Shelly

     
    |
    Mark as:
  • Currently Being Moderated
    Mar 27, 2012 3:26 AM   in reply to Shelly Goel
     
    |
    Mark as:
  • Currently Being Moderated
    Mar 27, 2012 4:52 AM   in reply to aklimets

    Hi,

     

    That's a good way. However, I am still not clear how to resolve the value not binding issue. Also, If the multifield is an array of name & title, how will we get the values of name & title for multifield[0] and so on.

     

    Thanks

    Shelly

     
    |
    Mark as:
  • Currently Being Moderated
    Apr 13, 2012 8:28 AM   in reply to pulsar440

    Hi, ive got the same requirement.

     

    i've come up with following Dialog structure which works fine at the first look. The values are stored correctly  ( path and label have the type String[] ) and my i am able to create link with my jsp.

    The only Problem is that the values are not shown in the dialog when i want to edit them. The second Problem is that the move-up and move-down buttons doesn't work, although the delete-button works fine.

     

    Does anybody does know a solution to that?

     

     

    <?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="TextLinkList"

        xtype="tabpanel">

        <items jcr:primaryType="cq:WidgetCollection">

            <static

                jcr:primaryType="cq:Widget"

                title="Text link list"

                xtype="panel">

                <items jcr:primaryType="cq:WidgetCollection">

                    <list

                        jcr:primaryType="cq:Widget"

                        fieldDescription="Click the '+' to add a new textlink"

                        hideLabel="true"

                        xtype="multifield">

                        <fieldConfig

                            jcr:primaryType="cq:Widget"

                            xtype="compositefield">

                            <items jcr:primaryType="cq:WidgetCollection">

                                <label

                                    jcr:primaryType="cq:Widget"

                                    name="./label"

                                    xtype="textfield"/>

                                <path

                                    jcr:primaryType="cq:Widget"

                                    name="./path"

                                    xtype="pathfield"/>

                            </items>

                        </fieldConfig>

                    </list>

                </items>

            </static>

        </items>

    </jcr:root>

     
    |
    Mark as:
  • Currently Being Moderated
    Apr 18, 2012 4:57 PM   in reply to angerwin

    Please read the above comments carefully. The normal multifield is an array of *primitive* values (e.g. strings). It is not an array of *objects*.

     

    The above CQ.CustomPathFieldWidget from Shelly puts objects into an array of strings by encoding the objects as json string. This can work, but I wouldn't really recommend it as I noted above.

     

    @Shelly: I guess the problem you mention is what I meant: if you want to read those objects in the jsp, you also have to parse the json strings there.

     
    |
    Mark as:
  • Currently Being Moderated
    Sep 20, 2012 2:07 AM   in reply to aklimets

    Hi everyone.

     

    I have also spent some time trying to built a custom ExtJS dialog that has a multifield with a textfield and a pathfield with the help of the "Using ExtJS Widgets" examples from the Package Share.

     

    My problem is that the example saves the values in a Stringarray (String[]) with a seperator character and the user can mess up the input by typing that seperator character.

    I have tried to alter my code to use JSON like Shelly suggested, but it didn't work and I got following error message in Firebug: "uncaught exception: cannot create Component: xtype 'ejstcustom' not found and no default supplied."

     

    Is there some way to save the values in a child-node of the component node in separate properties instead of the String[]?

     

    Here's my code:

    Ejst.CustomWidget = CQ.Ext.extend(CQ.form.CompositeField, {

     

        /**

         * @private

         * @type CQ.Ext.form.TextField

         */

        hiddenField: null,

     

        /**

         * @private

         * @type CQ.Ext.form.TextField

         */

        nameField: null,

     

        /**

         * @private

         * @type CQ.Ext.form.pathfield

         */

        linkPathField: null,

       

        constructor: function(config) {

            config = config || { };

            var defaults = {

                "border": false,

                "layout": "table",

                "columns":2

            };

            config = CQ.Util.applyDefaults(config, defaults);

            Ejst.CustomWidget.superclass.constructor.call(this, config);

        },

     

        // overriding CQ.Ext.Component#initComponent

        initComponent: function() {

            Ejst.CustomWidget.superclass.initComponent.call(this);

     

            this.hiddenField = new CQ.Ext.form.Hidden({

                name: this.name

            });

            this.add(this.hiddenField);

     

            this.nameField= new CQ.Ext.form.TextField({

                cls:"ejst-customwidget-2",

                listeners: {

                    change: {

                        scope:this,

                        fn:this.updateHidden

                    }

                }

            });

            this.add(this.nameField);

     

            this.linkPathField= new CQ.form.PathField({

                rootPath: "/content",

                predicate: "nosystem",

                showTitlesInTree: true,

                allowBlank: false,

                listeners: {

                    change: {

                        scope:this,

                        fn:this.updateHidden

                    },

                    dialogclose : {

                        scope:this,

                        fn:this.updateHidden

                    }

                }

               

            });

            this.add(this.linkPathField);

     

        },

       

        processInit: function (path, record) {

            this.nameField.processInit(path, record);

            this.linkPathField.processInit(path, record);

        },

     

        // overriding CQ.form.CompositeField#setValue

         setValue: function (value) {

            var parts = value.split("|");

            this.nameField.setValue(parts[0]);

            this.linkPathField.setValue(parts[1]);

            this.hiddenField.setValue(value);

        },

     

        // overriding CQ.form.CompositeField#getValue

        getValue: function() {

            return this.getRawValue();

        },

     

        getRawValue: function () {

            return this.nameField.getValue() + "|" +

                   this.linkPathField.getValue();

        },

     

        // private

        updateHidden: function() {

            this.hiddenField.setValue(this.getValue());

        }

     

    });

     

    // register xtype

    CQ.Ext.reg('ejstcustom', Ejst.CustomWidget);

    EDIT:

    Here is my 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" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"

        jcr:primaryType="cq:Dialog"

        warnIfModified="{Boolean}false"

        xtype="dialog">

        <items

            jcr:primaryType="cq:Widget"

            xtype="tabpanel">

            <items jcr:primaryType="cq:WidgetCollection">

                <first

                    jcr:primaryType="cq:Widget"

                    title="Multi-Field"

                    xtype="panel">

                    <items jcr:primaryType="cq:WidgetCollection">

                        <multi

                            jcr:primaryType="cq:Widget"

                            fieldLabel="Multi-Field"

                            hideLabel="{Boolean}true"

                            name="./multi"

                            xtype="multifield">

                            <fieldConfig

                                jcr:primaryType="nt:unstructured"

                                xtype="ejstcustom"/>

                        </multi>

                    </items>

                </first>

            </items>

        </items>

    </jcr:root>

     
    |
    Mark as:

More Like This

  • Retrieving data ...

Bookmarked By (1)

Answers + Points = Status

  • 10 points awarded for Correct Answers
  • 5 points awarded for Helpful Answers
  • 10,000+ points
  • 1,001-10,000 points
  • 501-1,000 points
  • 5-500 points