Santosh
Santosh

Reputation: 33

writing Custom xtype script using ExtJs to develop multifield in aem 6.2

Hi Im a novice in ExtJs script, im trying to develop custom multifield, i was able to understand the node creation part, but in the scripting part im unable to catch some of the things like in listener adding scope:this, fn:this.updatehidden i tried to google out the answer but i din get any satisfactory answer. so could any one please explain me the scope:this part and why we call superclass constructor in the initcomponent,any related resources are also welcome

Thanks in advance Love to code

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

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

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

    /**
     * @private
     * @type CQ.Ext.form.TextField
     */
    otherField: 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.allowField = new CQ.form.Selection({
            type:"select",
            cls:"ejst-customwidget-1",
            listeners: {
                selectionchanged: {
                    scope:this,
                    fn: this.updateHidden
                }
            },
            optionsProvider: this.optionsProvider
        });
        this.add(this.allowField);

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

    },

    // overriding CQ.form.CompositeField#processPath
    processPath: function(path) {
        console.log("CustomWidget#processPath", path);
        this.allowField.processPath(path);
    },

    // overriding CQ.form.CompositeField#processRecord
    processRecord: function(record, path) {
        console.log("CustomWidget#processRecord", path, record);
        this.allowField.processRecord(record, path);
    },

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

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

    // overriding CQ.form.CompositeField#getRawValue
    getRawValue: function() {
        if (!this.allowField) {
            return null;
        }
        return this.allowField.getValue() + "/" +
               this.otherField.getValue();
    },

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

});

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

Upvotes: 2

Views: 374

Answers (1)

Alexander
Alexander

Reputation: 20244

Class hierarchy, superclass constructor:

You are calling the superclass initComponent function because you want the functionality of the derived class's hierarchy to be available.

For example, if you want to construct an elephant:

  • First you set some properties, like "big" and "gray" and "female".
  • Then you construct a mammal with these properties.
  • The mammal class constructor will itself set some properties, like "has a head", and then call the animal constructor, so if you don't call the mammal constructor from elephant, you don't even get an animal at all!
  • The animal constructor will then inspect the properties and create an animal.
  • Then, the mammal class will add details that the animal class didn't cover, e.g. the breast.
  • After the mammal constructor has finished, the elephant constructor adds the details that the mammal class doesn't cover, for example the trunk.

If you would use the standard ExtJS syntax for this (not sure whether CQ has it's own "standard syntax"), the elephant definition would look like this:

Ext.define('Elephant',{
    extend:'Mammal',
    initComponent:function() {
        var me = this;
        // set config properties. Two possible calls:
        // "Ext.apply" overwrites config properties already defined by the subclass before constructor has been called
        // "Ext.applyIf" only sets config properties that have NOT been set by the subclass!
        // Since a MiniElephant subclass may want to set size:"small", we use applyIf here.
        Ext.applyIf(me,{ 
            size:'big',
            color:'gray'
        });
        me.callParent(arguments); // <- call constructor of superclass
        me.addTrunk(); // <- postprocessing
    },
    addTrunk:function() {
        var trunk = Ext.create('Trunk',{
            ...
        });
        me.getHead().add(trunk);
        // since addTrunk is called after the mammal constructor has been executed, 
        // the head is already initialized and the getHead function available!
    }
});

Ext.define('Mammal',{
    extend:'Animal',
    initComponent:function() {
        var me = this;
        // Every mammal has a head, so we force the property into here using "apply"!
        Ext.apply({
            hasHead:true,
            ...
        });
        me.callParent(arguments); // <- construct animal
        me.addBreast(); // <- add breast
    },
    getHead:function() {
        return this.headerEl;
    },
    ...
});

Listener scope:

A listener is a function. Every function has a so-called scope, which is the object that you will get when you access this from inside the function. As long as you don't use this inside your function, the scope doesn't matter to you.

By default, in JavaScript, the scope of a function is the object that the function is attached to, so if you have an object

var listeners = { update:function() { console.log(this); } };

and if you call the function like this:

listeners.update()

it will log the listeners object to the console; but if you do it like this:

var fn = listeners.update;
fn();

it won't! The scope of a function can be set if you call the function:

listeners.update.call(myScope, firstParameter, secondParameter, ...)

or if you apply it:

listeners.update.apply(myScope, parameterArray)

(Good to remember: Apply takes the Array!)

Since, in ExtJS, the listeners configuration is processed by an Observable mixin, which puts the functions into specially crafted sub-objects, the default scope won't make sense at all to an ExtJS programmer, so they have changed it. And for convenience, ExtJS has added a config property that can be used by the programmer to define his intended scope of the function.

So if you define a panel and add a field inside:

Ext.apply(me, {
    items:[{
        xtype:'textfield',
        listeners:{
            update:function() {
                console.log(this); // <- returns the panel, because...
            },
            scope:me // <- we are scoping to the panel!
        }
    }
});

Upvotes: 2

Related Questions