Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multifield component issue

Tags:

aem

I am creating a multifield component having 2 textfields. Following 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"
    jcr:primaryType="cq:Dialog"
    title="dialog"
    xtype="dialog">
    <items jcr:primaryType="cq:WidgetCollection">
        <links
            jcr:primaryType="cq:Widget"
            fieldLabel="QuickLinks"
            name="./links"
            xtype="multifield">
            <fieldConfig
                jcr:primaryType="cq:Widget"
                xtype="multifield">
                <items jcr:primaryType="cq:WidgetCollection">
                    <title
                        jcr:primaryType="cq:Widget"
                        fieldLabel="Title"
                        hideLabel="{Boolean}false"
                        name="./jcr:title"
                        xtype="textfield"/>
                    <url
                        jcr:primaryType="cq:Widget"
                        fieldLabel="URL"
                        name="./jcr:url"
                        xtype="textfield"/>
                </items>
            </fieldConfig>
        </links>
    </items>
</jcr:root>

I am able to edit the content, and the content gets saved. However I have 2 problems - 1) When the dialog loads, it is empty always, and it doesnt show the saved content when I reopen the dialog 2) The up and down arrows are not working any more. Any suggestions to fix these is highly appreciated. Thank you very much.

like image 917
jpr Avatar asked Oct 10 '14 02:10

jpr


2 Answers

The multifield xtype's field config takes only one item (i.e you can have one textfield in it. When multiple values are configured they will be stored as a multivalued property called links and when only one value is configured it'll be stored as a single valued property called links). The entire data configured in your multifield will be stored as links property in your node. You won't be able to get them as "jcr:title" and "jcr:url".

You should create a custom xtype say "linksXtype" that stores the "jcr:title" and "jcr:url" as a single string separated by some pattern say "***" ("jcr:title***jcr:url").

You can find the details of creating a custom xtype here : link

The xtype can be created like this:

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

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

/**
 * @private
 * @type CQ.Ext.form.ComboBox
 */
jcrtitle: null,

/**
 * @private
 * @type CQ.Ext.form.TextField
 */
jcrurl: 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.jcrtitle = new CQ.Ext.form.TextField({
        fieldLabel:"Jcr url",
        cls:"ejst-customwidget-1",
        listeners: {
             change: {
                scope:this,
                fn:this.updateHidden
            }
        },
        optionsProvider: this.optionsProvider
    });
    this.add(this.jcrtitle);

    this.jcrurl = new CQ.Ext.form.TextField({
        fieldLabel:"Jcr Title",
        cls:"ejst-customwidget-2",
        listeners: {
            change: {
                scope:this,
                fn:this.updateHidden
            }
        }
    });
    this.add(this.jcrurl);

},


// overriding CQ.form.CompositeField#setValue
setValue: function(value) {
    var parts = value.split("/");
    this.jcrtitle.setValue(parts[0]);
    this.jcrurl.setValue(parts[1]);
    this.hiddenField.setValue(value);
},

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

// overriding CQ.form.CompositeField#getRawValue
getRawValue: function() {

    return this.jcrtitle.getValue() + "***" +
           this.jcrurl.getValue();
},

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

// register xtype
CQ.Ext.reg('linksXtype', Ejst.CustomWidget);

change the dialog.xml to something like this

<?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="dialog"
xtype="dialog">
<items jcr:primaryType="cq:WidgetCollection">
    <links
        jcr:primaryType="cq:Widget"
        fieldLabel="QuickLinks"
        name="./links"
        xtype="multifield">
        <fieldConfig
            jcr:primaryType="cq:Widget"
            xtype="linksXtype">
        </fieldConfig>
    </links>
</items>
</jcr:root>

To fetch the values iterate over the string array stored as links property and split each string by "***"

EDIT :

Adobe consultancy services under its ACS-Commons package provides a more elegant multifieldpanel widget to handle this use case. It simplifies the approach and eliminates the need to write a custom xtype for every combination of required fields. The data is stored in form of JSON format and comes with taglibs to extract data from the node. Link : http://adobe-consulting-services.github.io/acs-aem-commons/features/widgets.html#multi-field-panel-since-150

like image 176
Sharath Madappa Avatar answered Sep 25 '22 12:09

Sharath Madappa


As Sharath says, you'll need to define your own custom XType, rather than putting multiple fields in the multifield itself.

As an alternative to concatenating fields within a String[] property, another approach is to create child nodes for each field added, e.g. rather than:

<links
    link="[Example|http://example.com,Google|http://google.com]"/>

You would end up with:

<links>
    <link_1
        title="Example"
        url="http://example.com"/>
    <link_2
        title="Google"
        url="http://google.com"/>
<links>

You can read the values back without needing to parse them from a String value. It also means that things like rollout which update pathfields should work as standard.

The code is too long to produce here in full, but there's a nice starting point of this on the Adobe forums here. (It has an Adobe copyright notice, but posted by a user — not sure of it's official status, but good as a reference implementation; EDIT: possibly related to the Citytechnic MultiCompositeField on Github, as spotted by ery).

The sample above also takes the same approach as the multifield itself — i.e. it reads from a fieldConfig node of the composite & creates a property for each entry on the child nodes it creates.

This makes the composite field completely reusable, as you only need one composite XType no matter how many variations you want to create, i.e. it would allow you to take the approach you outline in the question:

<links
    jcr:primaryType="cq:Widget"
    fieldLabel="QuickLinks"
    name="./links"
    xtype="mtmulticompositefield">
        <fieldConfigs jcr:primaryType="cq:WidgetCollection">
            <title
                jcr:primaryType="cq:Widget"
                fieldLabel="Title"
                hideLabel="{Boolean}false"
                name="./jcr:title"
                xtype="textfield"/>
            <url
                jcr:primaryType="cq:Widget"
                fieldLabel="URL"
                name="./jcr:url"
                xtype="textfield"/>
        </fieldConfigs>
    </links>

It also allows you to use more complex XTypes as children, e.g. images, without any further work.

like image 43
anotherdave Avatar answered Sep 25 '22 12:09

anotherdave