Best practice for overriding classes / properties in ExtJS? - extjs

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?

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

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.

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...
}
});

Related

How can I run the same setup function on every view?

Many of the views in my application need to be "collapsible". To the user this means that you can click an arrow to collapse or expand the view's contents.
When creating a view I need to be able to easily say, "This view should be collapsible," and then run the appropriate setup code (which essentially means adding the .collapsible class to the view's wrapper and inserting a dom element that looks like this: <div class="toggle"></div>
Suggestions on ways to pull this off seamlessly? I'm currently using Backbone, Backbone.Marionette, and Underscore.
I do this with another application that doesn't use Backbone. In that application every action results in a page refresh, so I just use jQuery to look for all elements with the .collapsible class and do my setup that way.
EDIT:
I'm using Backbone.Marionette.CompositeView for these particular views, if that helps.
I've done similar thing in my project by extracting such functionality into mixins. There're different approaches to implementing mixins in Backbone. Take a look here or here
You can create parent view that extends from Marionettes compositeView and add your common functionallity there, and have your project views extend from this parent view.
var CollapsibleView = Backbone.Marionette.CompositeView.extends({
variable1: 1,
var2: true,
initialize : function() {
// your code here
},
helperfunction : function () {
// other helpful function
}
});
var MySpecificView = CollapsibleView.extends({
mySpecificFunction : function () {
// some specificView functionality
}
});
var myProjectView= new MySpecifcView();
myProjectView.helperfunction(); /// function from the parent
myProjectView.mySpecificFunction(); /// function from the specificView
/// you also have the functionality added on the initialization of the collpasibleView

For what do I use the appProperty property?

I recently stumpled over the appProperty within the the Ext.app.Application class and wondered why would I use it. I would require access to App instance anyway to then access a variable that again contains the instance? Maybe I am stupied but for what is this property?
I guess you have a misunderstanding here; The name property just defines a namespace of the Application along with a getter Method for it (getApplication()) but it will not provide you with the current instance of that application unless you call the getter or use the new appProperty.
Lets say you have the following application
Ext.application({
name: 'App',
appProperty: 'instance',
launch: function() {
// some more code
}
});
the you can access this application from any Component by calling either
App.getApplicatio();
or
App.instance
Where the second will be bit faster cause it is no method call and for sure you can define the name of this property. So I guess you see this property is quite useful!
Note that a namespace is always a object in javascript. That is the
reason why you are able to place properties into it.

Using multiple plugins in Backbone.js

The plugin model in Backbone.js is really nice, but one thing I'm wondering about is whether it's possible to use multiple plugins without modifying any of the plugin source.
For example, say I've written two plugins for the Collections:
MyBetterCollection = Backbone.Collection.extend({
coolNewFeature: function () {
console.log('This feature is great.');
}
});
MyWayBetterCollection = Backbone.Collection.extend({
wayCoolerNewFeature: function () {
console.log('This feature is even better.');
}
});
I can see some potential issues already, if, for example, both plugins override something like the add method. But having to modify third-party plugins would be a bummer:
MyWayBetterCollection = MyBetterCollection.extend({
...
});
Is there a good approach to handling this situation?
There isn't an easy way to do this. Since there is no traditional inheritance in JavaScript, it's difficult to provide this facility.
You'd have to find plug ins designed to work with each other or fork them and make them compatible.
You could also provide your own extend method that would use the interceptor pattern or some type of monkey patching to provide access to overwritten methods from previous prototypes that were overwritten.
The two collection you've defined above are totally separate from each other. They simply inherit the methods from Backbone.Controller, and if you defined methods with the similar name, then you overwrite those.
Think about Backbone as a class inheritance in other programming languages. Basically, you extend Backbone.Collection as you would do with other languages.
As a result, you can call the superclass, like this
var MyCollection = Backbone.Collection({
toJSON: function() {
var toJSON = this.constructor.__super__.toJSON.call(this);
toJSON.extra = 'my extra value';
return toJSON;
});
Even if you extend your own collection, the logic remains.

backbone.js: encapsulating view functions?

Pretty new to backbone.js so forgive me of my ignorance. I'm wondering, is there a way to encapsulate functions within the View class specifically?
I ask because when setting default events...
events {
'click .something' : 'doSomething'
}
... I'd prefer to have doSomething be nested in an encapsulating object for optimal organization. For example:
ui: {
doSomething: function() {}
}
But then I can't seem to get the default events to work.
events {
'click .something' : 'ui.doSomething' // this doesn't work
}
Any help is greatly appreciated. Or, if you can tell me why I shouldn't be doing this then I'd appreciate that, as well. Thanks!
Looking through the source that binds the events (delegateEvents) which is called from the constructor, it is pretty clear that it works on variables with in the scope of the object.
http://documentcloud.github.com/backbone/docs/backbone.html#section-118
You could, however, override delegateEvents to be a bit smarter... You could parse the value for dots and chain your tokens. You could even check the type of the value and use an actual function in place of the string. That might give you better control the way you want.
More info on the delegateEvents function: http://documentcloud.github.com/backbone/#View-delegateEvents

backbone.js set model internal attributes hash directly

Using Backbone.js I know it's highly recommended to set a model's property using the set method, and NOT by directly modifying the attributes internal hash.
However, apart from not firing the "change" event automatically, what other cons or "side-effects" are there in modifying the internal hash directly?
The problem I'm facing is that while the set method takes an object literal, I need to assign the left hand side using a variable determined at run-time. Thanks.
myModel.set({
myProperty : myValue; //myProperty is a variable, so this is invalid syntax
})
//vs
myModel.attributes[myProperty] = myValue; //myProperty is a variable that can be evaluated
Well, if you look at the annotated source code, you'll find that set does a lot.
What if you extended Backbone.Model with a function that does it for you:
Backbone.Model.prototype.setByName = function(key, value, options) {
var setter = {};
setter[key] = value;
this.set(setter, options);
};
Then, you can just do what you want directly on the model:
var model = new Backbone.Model();
model.setByName(myProperty, "bar");
That feels like a better solution to me.
Edit
As #earl3s pointed out, this is no longer necessary in more recent versions of Backbone. Today, you can just call model.set(myProperty, "bar") and it does what you want.
In the annotated source code mentioned by Brian Genisio you can read the following lines:
"Handle both "key", value and {key: value} -style arguments.".
So you can just use model.set(myProperty,"bar",options).
Perhaps they've added this feature after the post of Brian Genisio.. i dunno.

Resources