Extjs Extending

extjs

Mixins
Plugins
Overrides

https://riptutorial.com/extjs/example/19284/extend-vs-override
https://docs.sencha.com/extjs/6.0.2/guides/core_concepts/components.html

What are three different ways to customize Ext JS (What are extensions? What are overrides? What are plugins?) ?

  1. Extension: Create a new class with added or modified behavior.
  2. Override: Globally alter the behavior of an existing class (useful for patching etc).
  3. Plugin: Augment and add behavior to an Ext.Component instance (but not tied to a class).

Example of using Ext.extend?

PuffButton = Ext.extend(Ext.Button, {
    constructor: function(config) {
        // Remember to call the base class method
        PuffButton.superclass.constructor.apply(this, arguments);

        // Add listener for the button click event
        this.on('click', function() { this.el.puff();}, this);
    }
});

In the above code, we create a new class named PuffButton. We can create the "puff" button:

new PuffButton({width: 130, text: "Puff", renderTo: Ext.getBody()});

Notice that we did not register the xtype so we cannot create an instance of PuffButton via configuration. If we want to, we can register the xtype:

Ext.reg('mypuffbutton',PuffButton );

Example of overriding?

Ext.override(Ext.Button, {
    onClick: Ext.Button.prototype.onClick.createSequence(function() {
        this.el.puff();
    })
});

We use override to alter the behavior of an existing class.

How can we create our own plugin?

A plugin is any type of object that has an init method.

var Puffable = function() {
    this.init = function(cmp) {
        cmp.on("afterrender", function() {
            this.el.on("click", this.el.puff, this.el);
        });
    };
};

new Ext.Button({
    text: "Plugin Puff",
    renderTo: Ext.getBody(),
    plugins: new Puffable()
});

Example of using afterRender and onDestroy:

Ext.ns('Ext.ux');
Ext.ux.Clock = Ext.extend(Ext.BoxComponent, {
    afterRender: function() {
        var size = Math.min(this.getHeight(), this.getWidth());
        this.bgEl = this.el.createChild({
            tag: 'img',
            cls: 'ext-ux-clock-img',
            src: this.clockBgUrl,
            width: size,
            height: size
        });

        this.canvas = Raphael(this.el.dom, size, size);
        this.drawHands();
        this.on('resize', this.handleResize, this);
        this.timer = setInterval(this.drawHands.createDelegate(this), 1000);

        // Call super class
        Ext.ux.Clock.superclass.afterRender.apply(this, arguments);
    },

    onDestroy: function() {
        // Call superclass
        Ext.ux.Clock.superclass.onDestroy.apply(this, arguments);
    }
});

How can we design for configurability?

MyTip = Ext.extend(Ext.Tooltip, {
    fadeDuration: 200,
    onMouseLeave: function() {
        this.el.fadeOut(this.fadeDuration);
    }
}

initComponent: function() {
    if (! this.tpl) {
        this.tpl = new Ext.Template('<div>{foo}</div>');
    }
}

MyClass = Ext.extend(Ext.Toolbar, {
    noDataText: 'No data to display',
    constructor: function() {
        this.add({
            text: this.noDataText
        });
    });
});

How can we document our code?

/**
 * @class MyClass
 * @extends Ext.Panel
 * @constructor 
 * @param {Object} config The config object
 */
MyClass = Ext.extend(Ext.Panel, {
    //
});

How can we build our own custom component?

  1. https://docs.sencha.com/extjs/6.0.2/guides/core_concepts/components.html
  2. https://www.sencha.com/forum/showthread.php?134001-Make-custom-component-in-extjs - done reading
  3. https://www.leeboonstra.com/Ext-JS/custom-ui-components-with-sencha/ (this one is for later version of Ext JS)
  4. https://stackoverflow.com/questions/6153362/how-to-create-custom-extjs-form-field-component - I need to read this
  5. https://docs.sencha.com/extjs/6.2.0/classic/Ext.form.field.Field.html - This is important. I need to read this.
  6. https://livebook.manning.com/book/ext-js-in-action-second-edition/chapter-6/
  7. https://www.extjs-tutorial.com/extjs/extjs-components
  8. https://www.sencha.com/forum/showthread.php?153282-Creating-custom-form-field - I need to read this
  9. https://walkingtree.tech/creating-data-entry-intensive-application-using-ext-js-6-5-modern-toolkit/
  10. https://learnfromsaki.com/software/form-field-icon-plugin/
  11. https://docs.sencha.com/extjs/6.0.2/guides/components/widgets_widgets_columns.html
  12. https://www.bhaweshkumar.com/blog/2015/09/14/creating-widget-in-extjs-6/
  13. https://helpx.adobe.com/pt/experience-manager/6-4/sites/developing/using/widgets.html
  14. https://www.oreilly.com/library/view/learning-ext-js/9781784394387/ch07s05.html
  15. https://developer.mozilla.org/en-US/docs/Learn/Forms/How_to_build_custom_form_controls
  16. https://stackoverflow.com/questions/13267319/how-to-create-custom-widget-in-extjs-4
  17. https://stackoverflow.com/questions/6609275/extjs-4-how-to-extend-extjs-4-components
  18. https://stackoverflow.com/questions/33823075/multiple-instances-of-custom-component-in-extjs-4-2
  19. https://www.tutorialspoint.com/extjs/extjs_custom_events_listeners.htm
  20. https://gist.github.com/manuelselbach/dc63abd313694c594d480b163a5f3053 - what is this?
Ext.namespace('Ext.ux');
Ext.define('Ext.ux.PCNewbornWeight', {
     extend: 'Ext.form.FieldContainer', // Ext.form.FieldContainer, Ext.form.field.Base
     mixins: {
         field: 'Ext.form.field.Field'
     },
     alias : 'widget.PCNewbornWeight',
     prefix: 'PC_NEWBORN_',
     border: false,
     constructor: function (config) {
         this.callParent(arguments); // calls Ext.panel.Panel's constructor
     },
     initComponent: function() {

        this.items = [
            {
                layout: 'hbox',
                border: this.border,
                items: [
                     {
                         xtype: 'textfield',
                         fieldLabel: 'Grams (g)',
                         labelAlign: 'top',
                         id: this.prefix + 'GRAMS'
                     },{
                         xtype: 'textfield',
                         fieldLabel: 'Pounds (lbs)',
                         labelAlign: 'top',
                         id: this.prefix + "_POUNDS"
                     }
                ]
            }
        ];
        this.callParent();

        this.initField();
     },
 });

How can we accomplish the label above field behavior?

Ext JS form fields have the labelAlign configuration setting. However, in order for it to work, we need to use the layout 'hbox'. We need to specify this on the container, or possibly the form field itself.

How can we display the red asterisk to indicate a mandatory field?

Because we display the label above the field, we managed to display the red asterisk by creating the field label dynamically using some code:

    var isMandatory = false;
    isMandatory = ((tjcMandatory == 'Y') || (cmsMandatory == 'Y')) ? true : false;
    var completeLabel = '<span id="' + uiElementId + '_MAN' + '" class="mandatoryMarkerContainer">';
    if (isMandatory) {
        completeLabel = completeLabel + '*';
    } else {
        completeLabel = completeLabel + '&nbsp;';
    }
    completeLabel += '</span>' + screenLabel;
    item.fieldLabel = completeLabel;

The mandatoryMarkerContainer class:

    display: inline-block;
    width: 6px; /* This is to get the red asterisk mandatory marker to be displayed lined up correctly */
    color: red;

Additionally, in our CSS, we also override .x-form-item-body:

.x-form-item-body {
    padding-left: 6px; /* This is to get the red asterisk mandatory marker to be displayed lined up correctly */
}

What are the steps to extend an existing Ext JS form field component?

1.  Define a new class, override the initComponent method, and call the parent class' initComponent method:

Ext.define('Cookbook.DisplayPanel', {
    extend: 'Ext.panel.Panel',

    initComponent: function(){
        // call the extended class' initComponent method
        this.callParent(arguments);
    }
});

2. Add our own component configuration to the initComponent method by applying it to the class itself:

initComponent: function(){
    // apply our configuration to the class
    Ext.apply(this, {
        title: 'Display Panel',
        html: 'Display some information here!',
        width: 200,
        height: 200,
        renderTo: Ext.getBody()
    });

    // call the extended class' initComponent method
    this.callParent(arguments);
}

3.  Next, we override the onRender function, which is used to render the component to the page. In our override, we immediately call the parent's onRender method, so the field is fully rendered before our code is executed. We then use the Ext.core.DomHelper class to insert a new div element, after the textfield, containing the value from the component's infoText property:

Ext.define('Cookbook.InfoTextField', {
    extend: 'Ext.form.field.Text',
    onRender: function(){
        this.callParent(arguments);

        // insert our Info Text element
        Ext.core.DomHelper.append(this.getEl(), '<div>' + this.infoText + '</div>');
    }
}, function(){
    console.log('Cookbook.InfoTextField defined!');
});

4. We can now create our new InfoTextField class wherever we like and display any value that we would like using the infoText config option, like this:

var infoTextField = Ext.create('Cookbook.InfoTextField', {
    renderTo: Ext.getBody(),
    fieldLabel: 'Username',
    infoText: 'Your Username must be at least 6 characters long.'
});

infoTextField.show();

Extending:

Application.PersonnelGrid = Ext.extend(Ext.grid.GridPanel, {
     border:false
    ,initComponent:function() {
        var config = {
           store:new Ext.data.Store({...})
          ,columns:[{...}, {...}]
          ,plugins:[...]
          ,viewConfig:{forceFit:true}
          ,tbar:[...]
          ,bbar:[...]
        }; // eo config object

        // apply config
        Ext.apply(this, Ext.apply(this.initialConfig, config));

        Application.PersonnelGrid.superclass.initComponent.apply(this, arguments);
    } // eo function initComponent

    ,onRender:function() {
        this.store.load();

        Application.PersonnelGrid.superclass.onRender.apply(this, arguments);
    } // eo function onRender
});

Ext.reg('personnelgrid', Application.PersonnelGrid);
MyApp.MyPanel = function(config) {
    MyApp.MyPanel.superclass.constructor.call(this, Ext.applyIf(config, {title: 'default title'}));
};
Ext.extend(MyApp.MyPanel, Ext.Panel, {
    // override methods go here
});
Ext.reg('mypanel', MyApp.MyPanel);

MyApp.eventManager = new Ext.util.Observable();
MyApp.eventManager.addEvents('customEventName');
MyApp.eventManager.fireEvent('customEventName',node);
MyApp.eventManager.on('customEventName', grid.loadNodeData.createDelegate(grid));

Ext.override(Ext.data.Store, {
    getByName: function(name) {
        this.getAt(this.findExact('name', name));
    }
}

How to extend ExtJS / How to implement pre-configure classes?

function MyCombo (config) {
    // set up your datasource here ..
    MyCombo.superclass.constructor.call(this, config);
}
Ext.extend(MyCombo, Ext.form.ComboBox, {
  typeAhead: true,
  loadingText: 'Searching...',
  forceSelection: true,
  allowBlank: false,
  minChars: 3,
  pageSize: 10,
  hideTrigger: true
});
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License