PDA

View Full Version : Convenience methods for creating Classes and wrapping App configs



dnalot
20 Feb 2008, 10:03 PM
Have been rolling out a lot of production js lately and these have been very helpful. Whether just writing application code to a given namespace or deriving a class with a mixin, these have both reduced copy/paste and made it easier to remember what my code is doing by readability.

The applyMixin method automatically gets your namespace, passes it to a function that is expected to return your application object, and defers to your callback with namespace scope. NB: The module parameter your mixin function receives is only useful within callback functions. Additionally, module and mixin arguments will accept type Object.

/**
* intended to wrap module files
* @param {String/Object} module value automatically namespaced
* @param {Function/Object} mixin passed single argument of module expected to return Object
* @param {Function} callback (optional) called after mixin has been applied to module
* @param {Object} scope (optional) of the callback defaults to module
* @return {Object} module
*//*
* <code>
* Ext.ux.applyMixin('Ext.ux.application', function( $module ) { return {
* //application methods and properties
* init : function() {}
* }; }, function() { Ext.onReady(this.init); });
* </code>
*/
Ext.ux.applyMixin = function( module, mixin, callback, scope ) {/*...*/};


extendMixin mirrors applyMixin and additionally passes your superclass prototype as a parameter to your mixin function and automatically registers an xtype if your last argument provided is a String. If your mixin provides a Constructor property, that method will be wrapped by your Class constructor. Check out my source (http://www.docudesk.com/scripts/domcmp.js) from another extension for an extended example of this method.

/**
* intended to wrap class files
* @param {String/Function} subclass value automatically namespaced
* @param {Function} superclass
* @param {Function/Object} mixin passed subclass and superclass.prototype expected to return Object
* @param {Function} callback (optional) called after mixin has been applied to module
* @param {Object} scope (optional) of the callback defaults to subclass
* @param {String} xtype (optional) final String argument registers xtype
* @return {Function} subclass
*//*
* <code>
* Ext.ux.extendMixin('Ext.ux.NewClass', Ext.Superclass, function( $super, $class ) { return {
* //static methods and properties
* Constructor : function() {
* //...
* $super.constructor.apply(this, arguments);
* //...
* }; }, 'xtype');
* </code>
*/
Ext.ux.extendMixin = function( subclass, superclass, mixin, callback, scope ) {/*...*/};


Updates
-- 2008-02-24 --
[added] - Support for classes without namespaces by simplifying underlying initialization
-- 2008-02-26 --
[changed] - Reversed arguments passed to mixin by extendMixin
[added] - Support for reading Constructor from mixin by extendMixin
-- 2008-03-05 --
[added] - Automatic ctype property identifies fully qualified class name useful for firebugging

tof
22 Feb 2008, 2:17 AM
Wow !
I'm not a genius, that's for sure, but I'm quite used to extjs & js by now...
And I can't see the point of your packge !

This seems interesting though, and since you "have been rolling out a lot of production js lately and these have been very helpful", i'd love to have a clue on what/why you use this.
So, if you've got some time, could you give me some hints ?

Btw, you seems to use the "$" (dollar) sign in your vars. Is there any meaning to this ?

Thanks for your answers !

dnalot
22 Feb 2008, 9:02 AM
regarding '$' variables, these are an indication to me of those variables which have top level scope w/in a function containing other closures. for explanation i will provide a basic example from ext's source and point to one spot from my linked example.

Ext.Viewport:

Ext.Viewport = Ext.extend(Ext.Container, {

initComponent : function() {
Ext.Viewport.superclass.initComponent.call(this);
//...
},

fireResize : function(w, h){/*...*/}

});
Ext.reg('viewport', Ext.Viewport);

Ext.ux.Viewport:

/**
* this version automatically calls Ext.namespace('Ext.ux')
* especially important for deeper namespacing e.g. Ext.ux.layout.CustomLayout
* or a project where include order may need to be changed down the road
*/
Ext.ux.extendMixin('Ext.ux.Viewport', Ext.Container, function( $class, $super ) { return {

/**
* $super is provided as a shortcut for Ext.Container.prototype or, w/in a closure as above, for
* Ext.ux.Viewport.superclass or, preferably, this.constructor.superclass
* b/c having to copy/paste either of the other alternatives repeatedly
* has lead to inconspicuous errors from using the wrong class or superclass
*/
initComponent : $super.createSequence(function() {/*...*/}),

fireResize : function(w, h){/*...*/}

//extendMixin automatically registers an xtype if provided
}; }, 'viewportux');

another example:

//in this case two classes are derived from the same mixin declared elsewhere
Ext.ux.extendMixin('Ext.ux.DomComponent', Ext.BoxComponent, mixin, 'dom');

//this class overrides mixin and superclass methods in a callback
Ext.ux.extendMixin('Ext.ux.Canvas', Ext.Container, mixin, function() {
//the callback's scope defaults to the class itself
this.override({

initComponent : this.superclass.initComponent.createInterceptor(function() {/*...*/}),

initAutoEl : this.prototype.createSequence(function() {/*...*/}),

getResizeEl : function() {/*...*/}

});
}, 'canvas');

tof
22 Feb 2008, 9:12 AM
Ok, you made things clear.
I like the use you make of "$", good idea.

$super is here very convenient, indeed that's a hell to rewrite the whole name each time.

The automatic namespace thing is interesting too.

Thanks for sharing & explaining !

dnalot
22 Feb 2008, 6:08 PM
Thanks for the feedback. I've been watching your GUI Builder -- great work!

nico
1 Jul 2008, 12:27 AM
Hi Toland,

I use the extendMixin function an it does its job very well. It really simplifies access to the class and its static members (that I use here and there) and of course calls to methods of the superclass.

Thank you very much for sharing,
Nico