Multifield component issue
Asked Answered
L

3

5

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.

Leonhard answered 10/10, 2014 at 2:13 Comment(0)
H
6

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

Hiro answered 10/10, 2014 at 7:33 Comment(3)
Thanks. Just wanted to clarify that I am able to read my data as "jcr:title" and "jcr:url". Please see the link here http://pbrd.co/1vT2L4H. The problem I have is that the textfield components are not able to read these values and populate in the dialog when I try to edit.Leonhard
The only drawback with concatenating the values is that you may have problems with things like rolling out live copies — e.g. if your site structure has es as a live copy of en, normally pathfields would update this path automatically on rollout, but I think it has trouble with concatenated fields, as it treats the whole composite as a straight textfield.Lowelllowenstein
@Leonhard The values might get stored, but i don't think it will work the way you want it to out of the box. If you check out the code for multifield, it adds items of the xtype mentioned on the field config node. So when you open the dialog the property stored with name you supplied in the multifield is iterated upon and set for each item in the multifield. With your current set up the values aren't stored in the form expected by multifield's code. You will have to create custom xtypes AFAIK.Hiro
L
2

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.

Lowelllowenstein answered 10/10, 2014 at 8:41 Comment(8)
Will the values of the child nodes also get read automatically with the way dialogs work out of the box ? or do we have to explicitly read them and populate the dialog on load.Hiro
@SharathMadappa Yes, they get read back in as normal when the dialog opens alright without any extra work.Lowelllowenstein
How did you apply this? i can't make it work. I need this in the page properties but also tested in a dialog of a common component with out luck. I can't see even the + sign to add more fields to the custom xtype. Maybe something is missing?Grammalogue
@Grammalogue it's hard to debug your issue without seeing some code… maybe raise a new question with your dialogue sample?Lowelllowenstein
@Lowelllowenstein what i need is to apply this in a custom tab on the page properties. I have added the js file to my clientlib, but it's not working.Grammalogue
@Grammalogue I understand what you're aiming for, but it's hard to tell what's wrong from "It's not working" —if you post a minimal complete verifiable example as a new question it would be easier to track down the problems that you're having — could you try it in a vanilla install, show the code you're using & list any errors you're seeing (both in browser dev console & crx-quickstart logs)?Lowelllowenstein
The code above looks very similar to multicomposite addon developed by Citytechnic: github.com/Citytechinc/multicomposite-addon/blob/develop/src/… .Not sure what was the original version but the code on github is being regularly updated.Batavia
@Batavia It does! & the fact that the Citytechnic version has someone called "MT" leaving to-do notes inline & the version I found was called "mtmulticompositefield" feel related too :) Thanks, I'll add a note to the answer itself.Lowelllowenstein
H
0

I know problem is resolved by this time I commenting but this is just for reference. 1) When the dialog loads, it is empty always, and it doesnt show the saved content when I reopen the dialog

Answer : I was using extjs to create multifield for my dialog and in my extjs set() function code was like

setValue: function(value) {

    var link = JSON.parse(value);
    this.websiteName.setValue(link.text);
    this.websiteLinks.setValue(link.text);
    this.hiddenField.setValue(value);
},

but code should be

setValue: function(value) {

    var link = JSON.parse(value);
    this.websiteName.setValue(link.field1Name);
    this.websiteLinks.setValue(link.field2Name);
    this.hiddenField.setValue(value);
},

Just correcting this your dialog will show populated values. Also check your name property in dialog. It should be correct.

2) The up and down arrows are not working any more.
This issue is with your js file mostly. What I experienced. Whenever clicks are not working, check your js for errors in developer tools in browser. With a single syntax error your js stop working and clicks too.

Hope this helps someone :)

Heliograph answered 7/12, 2015 at 21:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.