8 Replies Latest reply on Sep 20, 2012 2:07 AM by Andras Fejer

    How to build a custom multifield component?

    pulsar440

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

       

        • 1. Re: How to build a custom multifield component?
          Shelly Goel

          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>

          • 2. Re: How to build a custom multifield component?
            aklimets Adobe Employee

            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

            • 3. Re: How to build a custom multifield component?
              Shelly Goel Level 1

              Hi Aklimets,

               

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

               

              Thanks

              Shelly

              • 5. Re: How to build a custom multifield component?
                Shelly Goel Level 1

                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

                • 6. Re: How to build a custom multifield component?
                  angerwin

                  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>

                  • 7. Re: How to build a custom multifield component?
                    aklimets Adobe Employee

                    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.

                    • 8. Re: How to build a custom multifield component?
                      Andras Fejer Level 1

                      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>