Best practice for overriding classes / properties in ExtJS?
Asked Answered
E

3

33

I have an Ext.form.field.Text and I want to override the setValue function.

What is the recommended way to override this class functionality in ExtJS? Ext.override?

Exmoor answered 10/1, 2013 at 9:15 Comment(1)
I edited the question to be more constructive. Please re-open this question because the answer is very useful to the ExtJs community.Exmoor
C
80

For clarification: By real class modification I mean a intended permanent modification/extension of a class, which should always be done by extending a class. But it is not a temporary solution for just a specific problem (bug-fix, etc.).

You have at least four options how to override members of (Ext) Classes

  • prototype I guess is well known and allows you to override a member for all instances of a class. You can use it like

    Ext.view.View.prototype.emptyText = "";
    

    While you can't use it like

    // callParent is NOT allowed for prototype
    Ext.form.field.Text.prototype.setValue = function(val) {
        var me = this,
            inputEl = me.inputEl;
    
        if (inputEl && me.emptyText && !Ext.isEmpty(value)) {
            inputEl.removeCls(me.emptyCls);
            me.valueContainsPlaceholder = false;
        }
    
        me.callParent(arguments);
    
        me.applyEmptyText();
        return me;
    };
    

    Here's a JSFiddle

    This variant should not be used for real class modifications.

  • Ext.override does nearly the same then prototype but it fully applies to the ExtJS Class-system which allows you to use callParent()

    You can use it like

    // callParent is allowed for override
    Ext.override('Ext.form.field.Text', {
        setValue: function(val) {
            this.callParent(['In override']);
            return this;
        }
    });
    

    Here's a JSFiddle (c-p error fixed! Thanks to @nogridbag)

    Use case: I faced a (I think still existing) bad behavior of a radiogroup where ExtJS expect a object (key-value-pair) for correct setting of the value. But I have just one integer on my backend. I first applied a fix using Ext.override for the setValue() method and afterwards extend from radiogroup. There I just make a Key-Value-Pair from the given value and call the parent method with that.

    As @rixo mentioned this can be used for overriding a instance member. And may therefore be qualified for overriding even mixins (I never tested it myself)

    var panel = new Ext.Panel({ ... });
    Ext.override(panel, {
        initComponent: function () {
            // extra processing...
    
            this.callParent();
        }
    });
    

    This variant should not be used for real class modifications.

  • Extending a existent class to apply additional behavior & rendering. Use this variant to create a subtype that behaves different without loosing the original type.

    In the following example we extend the textfield with a method to change the labelcolor when setting a new value called setColored and override the setValue method to take care of removing a label color when setValue is called directly

    Ext.define('Ext.ux.field.Text',{
        extend: 'Ext.form.field.Text',
        widget: 'uxtextfield',
        setColored: function(val,color) {
            var me = this;
            if (me.settedCls) {
                me.removeCls(me.settedCls);
            }
            me.addCls(color);
            me.settedCls = color;
            me.setValue(val,true);
        },
        setValue: function(val,take) {
            var me = this;
            if (!take && me.settedCls) {
                me.removeCls(me.settedCls);
            }
            me.callParent(arguments);
            return me;
        }
    });
    

    Here's a JSFiddle

  • Overriding per instance will happen in really rare cases and might not be applicable to all properties. In such a case (where I don't have a example at hand) you have a single need for a different behavior and you might consider overriding a setting just per instance. Basically you do such things all times when you apply a config on class creation but most time you just override default values of config properties but you are also able to override properties that references functions. This completely override the implementation and you might allows don't have access to the basetype (if any exist) meaning you cannot use callParent. You might try it with setValue to see that it cannot be applied to a existing chain. But again, you might face some rare cases where this is useful, even when it is just while development and get reimplemented for productive. For such a case you should apply the override after you created the specific by using Ext.override as mentioned above.

    Important: You don't have access to the class-instance by calling this if you don't use Ext.override!

If I missed something or something is (no longer) correct, please comment or feel free to edit.

As commented by @Eric

None of these methods allow you to override mixins (such as Ext.form.field.Field). Since mixin functions are copied into classes at the time you define the class, you have to apply your overrides to the target classes directly

Callboy answered 10/1, 2013 at 9:33 Comment(12)
@Exmoor I made a edit which I think explain it all a bit better and more in deepCallboy
Wow, this is more than I asked for! Thank you!Exmoor
Great answer, but there's one minor caveat I want to mention. None of these methods allow you to override mixins (such as Ext.form.field.Field). Since mixin functions are copied into classes at the time you define the class, you have to apply your overrides to the target classes directly.Philo
@Philo I recently stumbled into exactly such a situation. But I didn't had enough time to evaluate this in deep. Applying a override has one huge downside, it applies global. This wasn't a option in my case. As soon as I have time I will take a deeper look how this may be done without a global override. Anyway, thanks for the feedback!Callboy
The Ext.override example seems to be incorrect. The JSFiddle example's override never gets executed and even if it does, it would thrown an error since "me" is not defined. I slightly modified the example so that it's working: jsfiddle.net/NFSwSArchival
@Archival Thanks a lot for that hint and for supplying a fixed fiddle. I didn't haven seen that I forgot to fix the copied the prototype there. To stay with 4.1.3 I copied your example to another one.Callboy
Another override method is to use Ext.define in combination with the override param instead of the extend param. That way you can use the class loading system and require overrides like you can require any other defined class.Hyalite
Is there a way to override a class with a new method only if that method doesn't already exist? I want to ensure that in a future upgrade to a later version of ExtJS that might implement this function, we will not lose functionality.Intermix
@Intermix I think you are talking about something like it's done in other languages where you need to use a keyword like override otherwise you get an error. ExtJS doesn't support this out of the box. But you can add a custom Ext.override for that, which only applies if the method not exist. I just wouldn't name it override ;) Another way is within the initComponent after the callParent call. You can now check if a method exist and and apply it if not. You would use Ext.applyIf for that. But you will never know if the function with the same name really does the same thing!Callboy
@Callboy Yes, this is pretty much what I'd like to do, the equivalent of the PHP method_exists function. Of course you will never know if the function does the exact same thing, but in my case (I want to override the Ext.data.TreeStore with a commitChanges method, which doesn't appear to be there in 4.1.3), I am pretty sure that if it is ever implemented in the future, it will do exactly what I need it to do :)Intermix
@Callboy The answer says "This variant should not be used for real class modifications" but it might be not clear what do you mean by "real class modifications".Genisia
Somewhat surprisingly maybe, but you can use Ext.override on instances -- not only class -- and have callParent working in overridden methods (as seen in the example in the doc of the function).Tim
T
3

The answer by @sra is great and was very helpful to me in gaining a deeper understanding of the override functionality available in Ext, but it does not include the way that I most commonly implement overrides which looks something like this:

Ext.define('my.application.form.field.Text' {
    override: 'Ext.form.field.Text'
    getValue: function () {
        // your custom functionality here
        arguments[1] = false;

        // callParent can be used if desired, or the method can be 
        // re-written without reference to the original
        this.callParent(arguments)
    }
});

I'm still using Ext 5 so I would then load this file in my Application.js and add it to the requires array there which applies the override to the app globally. I think Ext 6 projects include an override folder and simply adding this file to that folder ensures the override is applied.

Thematic answered 5/5, 2017 at 22:52 Comment(1)
Thx for detailed explanation and example.Quant
A
2

This is the only way that works for me in ExtJS 7.

Example:

app/desktop/overrides/Toast.js

Ext.define(null, {
    override: 'Ext.window.Toast',
    show : function () {
        this.callParent();
// Your custom code here...
    }
});
Ammoniacal answered 19/6, 2021 at 8:43 Comment(1)
More on anonymous classesExmoor

© 2022 - 2024 — McMap. All rights reserved.