1. #1
    Sencha User
    Join Date
    Nov 2008
    Location
    Lyon, France
    Posts
    223
    Vote Rating
    29
    christophe.geiser has a spectacular aura about christophe.geiser has a spectacular aura about

      0  

    Default towards Class config that does not fill the prototype

    towards Class config that does not fill the prototype


    Hi all,
    I like not to be forced to write setters and getters - and hence the idea of a config object that populates its own functions. However the current implementation has a problem imho with the fact that it places non-primitive Objects (like arrays and {}) in the prototype. Problems soon arise when multiple instance of the same class are being created...

    This is an attempt to provide a fix:
    - only primitive, functions or null configs are put in the prototype.
    - instead of using 'set/get' + configName, the setter and getters are just functions with the same name of the config. When called with an arguments, it will be the setter (returning this), without arguments, the getter. It is faster to write, and allows method chaining (see example below).
    - for types other than primitive, the configs are applied only the first time the setter/getter is called. The default config is still available, but it is applied to the instance and not the Class.
    - apply and updates functions work as without the fix.

    Use like this:
    Code:
    Ext.define('MyChart', {  
      extend: 'Ext.chart.Chart',  
      config: {
         width: 100,
         heigth: 100,
         anArray: [],
         anObject: {}
         },   ...
    and later: myChart.width(200).height(250) to set width and height or myChart.width() to get the width.

    The other side effect is that initConfig method does not need to be called for this to work (as long as you access you configs with the created getter)

    Cheers,
    C.

    Fix:
    Code:
    (function() {
    var  ExtClass = Ext.Class,
            prefix = "",
            config,
            prototype,
            data;
    
        function applyClass(config) {
              Ext.Object.each(config,function(name, value) { 
                name = prefix + name; 
                var nameMap = ExtClass.getConfigNameMap(name),
                    internalName = nameMap.internal,
                    primitiveName = nameMap.primitive,
                    initializedName = nameMap.initialized,
                    applyName = nameMap.apply,
                    updateName = nameMap.update,
                    setName = nameMap.set,
                    getName = nameMap.get,
                    hasOwnSetter = (setName in prototype) || data.hasOwnProperty(setName),
                    hasOwnApplier = (applyName in prototype) || data.hasOwnProperty(applyName),
                    hasOwnUpdater = (updateName in prototype) || data.hasOwnProperty(updateName),
                    optimizedGetter, customGetter,
                    //added
                    changeName = nameMap.changeEvent, 
                    isFn = Ext.isFunction(value),
                    isPrimitive = (value === null || Ext.isPrimitive(value) || isFn),
                    
                    fnName = nameMap.fn,
                    hasOwnFn = (fnName in prototype) || data.hasOwnProperty(fnName);
    
    
                //if (value === null || (!hasOwnSetter && !hasOwnApplier && !hasOwnUpdater)) {
                if(isPrimitive){    
                    prototype[internalName] = value;
                    prototype[primitiveName] = true;
                    prototype[initializedName] = true;
                }
                else {
                    prototype[primitiveName] = false;
                    prototype[initializedName] = false;
                }
    
    
                if (!hasOwnSetter && !isFn) {
                    data[setName] = function(value) {
                        var oldValue = this[internalName],
                            applier = this[applyName],
                            updater = this[updateName];
    
    
                        if (!this[initializedName]) {
                            this[initializedName] = true;
                        }
    
    
                        if (applier) {
                            value = applier.call(this, value, oldValue);
                        }
    
    
                        if (typeof value != 'undefined') {
                            this[internalName] = value;
                            if (updater && value !== oldValue) {
                                updater.call(this, value, oldValue);
                            }
                        }
                        return this;
                    };
                }
    
    
                if (!(getName in prototype) || data.hasOwnProperty(getName)) {
                    customGetter = data[getName] || false;
    
    
                    if (customGetter) {
                        optimizedGetter = function() {
                            return customGetter.apply(this, arguments);
                        };
                    }
                    else {
                        optimizedGetter =
                            isFn ? function() {return  this[internalName]()} 
                            : function() {return  this[internalName]};
                    }
    
    
                    data[getName] = function() {
                        var currentGetter;
    
    
                        if (!this[initializedName]) {
                            this[initializedName] = true;
                            this[setName](this.config[name]);
                        }
    
    
                        currentGetter = this[getName];
    
    
                        if ('$previous' in currentGetter) {
                            currentGetter.$previous = optimizedGetter;
                        }
                        else {
                            this[getName] = optimizedGetter;
                        }
                        
                        return optimizedGetter.apply(this, arguments);
                    };
                }
             if(!hasOwnFn) {
                    data[fnName] = function() {
                        if(arguments.length) {
                            this[initializedName] = true;
                            return (this.isModel && this.fields.map[fnName]) 
                                ? this.set(fnName, arguments[0])
                                : this[setName].apply(this, arguments)
                            }
                        if(!this[primitiveName]){    
                            if(!this.hasOwnProperty(this[initializedName])) {
                               this[setName](Ext.clone(this.getConfig(fnName))); 
                               this[initializedName] = true;
                                 }
                            }
                        return optimizedGetter.apply(this, arguments) 
                        }
               };
             })
        };
            
        ExtClass.getConfigNameMap= function(name) {
                var cache = this.configNameCache,
                    map = cache[name],
                    capitalizedName;
    
    
                if (!map) {
                    capitalizedName = name.charAt(0).toUpperCase() + name.substr(1);
     
                    map = cache[name] = {
                        internal: '__' + name + '__', 
                        fn: name,
                        initialized: '_is' + capitalizedName + 'Initialized',
                        primitive: '_is' + capitalizedName + 'Primitive',
                        apply: 'apply' + capitalizedName,
                        update: 'update' + capitalizedName,
                        'set': 'set' + capitalizedName,
                        'get': 'get' + capitalizedName,
                        doSet : 'doSet' + capitalizedName,
                        changeEvent: name.toLowerCase() + 'change'
                    };
                }
    
    
                return map;
            }
        ExtClass.getPreprocessor('config').fn = function(Class, dt){
                data = dt;
                config = data.config;
                prototype = Class.prototype;
    
    
            delete data.config;
            applyClass(config);
            Class.addConfig(config, true);
        }   
       Ext.override(Ext.base,{
        initConfig : function(config) {
            this.callParent([config]);
            var  hasConfig = this.configMap;
            for (name in config) {
                    if (hasConfig[name]) { delete config[name]}
                    }
            return this
            }
       })
    }());

  2. #2
    Sencha - Senior Forum Manager mitchellsimoens's Avatar
    Join Date
    Mar 2007
    Location
    Gainesville, FL
    Posts
    37,525
    Vote Rating
    871
    mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute

      0  

    Default


    You could just create an abstract component or an override to call initConfig in the constructor.
    Mitchell Simoens @SenchaMitch
    Sencha Inc, Senior Forum Manager
    ________________
    Check out my GitHub, lots of nice things for Ext JS 4 and Sencha Touch 2
    https://github.com/mitchellsimoens

    Think my support is good? Get more personalized support via a support subscription. https://www.sencha.com/store/

    Need more help with your app? Hire Sencha Services services@sencha.com

    Want to learn Sencha Touch 2? Check out Sencha Touch in Action that is in print!

    When posting code, please use BBCode's CODE tags.

  3. #3
    Sencha User
    Join Date
    Nov 2008
    Location
    Lyon, France
    Posts
    223
    Vote Rating
    29
    christophe.geiser has a spectacular aura about christophe.geiser has a spectacular aura about

      0  

    Default


    Quote Originally Posted by mitchellsimoens View Post
    You could just create an abstract component or an override to call initConfig in the constructor.
    Thanks for the answer.

    I am not sure I get why creating an abstract component would solve the problem of having non-primitive config object filling-up the prototype and hence shared by all class instance. But I am certainly missing something.
    Nevertheless, this is what I am trying to solve here

    Cheers
    C.

Thread Participants: 1