Ext JS

From PGWiki
Revision as of 14:27, 12 October 2020 by Potatogim (talk | contribs) (bind: 제목 수준 내리기)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Ext JS는 자바스크립트 프레임워크이다.

constructor와 initComponent의 호출 순서

theExtendedClass = Ext.extend(theParentClass, {  
    //reconfigurables 
    aaa: 'test',

    //end reconfigurables

    /** @private */
    constructor: function(config){
        config = config || {};

        Ext.apply(config, {
            // stuff
        });

        console.info('1. before constructor parent');

        // call the superclass's constructor
        theExtendedClass.superclass.constructor.call(this, config);

        console.info('4. after constructor parent');
    },

    /** @private */
    initComponent: function(config) {
        console.info('2. before initComponent parent');

        Ext.apply(this, {
            // stuff
        });

        // call the superclass's constructor
        theExtendedClass.superclass.initComponent.apply(this, arguments);

        console.info('3. after initComponent parent');
    }
});

TreeStore가 반복적으로 로딩되는 문제

TreeStore를 사전 적재(pre-loaded) 방식으로 한꺼번에 불러오는 과정에서 프록시를 Ajax로 하면 무한 로딩이 되는 문제가 있다.

정확히는 프록시의 JsonReader의 root 속성을 사용자 정의하는 경우가 문제이며, 이러한 경우에는 반환되는 데이터에서 자식 노드를 가리키는 필드 이름을 사용자 정의하는 root 속성의 값과 일치하게 해주면 된다.

스토어 프록시에서 JSON 형식으로 매개변수 전달하기

ExtJS 4.2.3부터 제공되는 옵션인 paramsAsJson을 사용하면 JSON 데이터를 바로 송신할 수 있다.

var myStore = Ext.create('Ext.data.Store', {
    model: 'myModel',
    //*
    proxy: new Ext.data.proxy.Ajax({
        url: '/api/user/list',
        reader: new Ext.data.JsonReader({
            root: 'entity'
        }),
        paramsAsJson: true,
    }),
    /*/
    proxy: {
        type: 'ajax',
        url: '/api/user/list',
        paramsAsJson: true,
        reader: {
            type: 'json',
            root: 'args'
        }
    },
    //*/
    listeners: {
        beforeload: function(store, operation, eOpts) {
            store.proxy.setExtraParam(
                'entity',
                {
                    potatogim: 1,
                }
            );

            console.log(operation);
        },
        load: function(store, records, success) {
            console.log(store);
        }
    }
});

위 방법 중 1안은 프록시를 재정의해서 사용하는 경우에 적합한 경우이고, 그게 아니라 단순 매개변수 변환이라면 2안이 조금 더 간단한 선택지가 될 수 있겠다.

폼 제출 시, JSON 형식으로 매개변수 전달하기

var myFormPanel = Ext.create('Ext.form.Panel', {
    id: 'myForm',
    frame: false,
    // jsonSubmit을 활성화하면 폼 데이터가 JSON 형식으로 제출된다.
    jsonSubmit: true,
    items: [
        {
            ...
        },
        {
            xtype: 'textfield',
            fieldLabel: "Name",
            id: 'username',
            name: 'username',
            allowBlank: false,
        }
    ]
});

Ext.getCmp('myForm').getForm().submit({
    method: 'POST',
    url: '/api/user/info',
    # submit() 호출 시점에 지정된 params 또한 JSON 형식으로 전달된다.
    params: {
        username: ...,
    },
});

프록시에서 스토어로 데이터 적재 전 변경하기

var myStore = Ext.create('Ext.data.Store', {
    model: 'myModel',
    proxy: {
        type: 'ajax',
        url: '/api/user/info',
        reader: {
            type: 'json',
            root: 'entity',
            totalProperty: 'count',
            getResponseData: function(response) {
                try {
                    var json = Ext.decode(response.responseText);

                    return this.readRecords({
                        entity: json.group,
                        count: json.count ? json.count : 0,
                        success: json.success,
                    });
                }
                catch(ex) {
                    var error = new Ext.data.ResultSet({
                        total: 0,
                        count: 0,
                        records: [],
                        success: false,
                        message: ex.message,
                    });

                    Ext.log('Unable to parse the JSON returned by the server');
                    return error;
                }
            }
        }
    },
    load: function(store, records, success) {
        ...
    }
});

모델의 필드 기본값으로 undefined를 사용하는 경우

Ext.override(
    Ext.data.field.Field,
    {
        defaultValue: undefined,
        convert: null,
    },
);

핸들러에 매개변수 추가하기

ExtJS 4

Ext.create(
    'Ext.panel.Panel',
    {
        name: 'panelBtn',
        layout: 'hbox',
        border: 0,
        items: [
            {
                xtype: 'button',
                text: 'Add',
                name:'addBtn',
                handler: Ext.bind(this.addBtnHandler, this, repeatsStore, true)
            }
        ]
    }
);

addBtnHandler:function(button, event, repeatsStore)
{
    ...
}

apply, applyIf, bind, override

apply

c에 있는 모든 속성들은 o에 복사한다.

defaultsnull이 아니면 default의 속성을 o에 복사한다.

    /**
     * Copies all the properties of config to the specified object.
     * Note that if recursive merging and cloning without referencing the original objects / arrays is needed, use
     * {@link Ext.Object#merge} instead.
     * @param {Object} object The receiver of the properties
     * @param {Object} config The source of the properties
     * @param {Object} [defaults] A different object that will also be applied for default values
     * @return {Object} returns obj
     */
    Ext.apply = function(object, config, defaults) {
        if (defaults) {
            Ext.apply(object, defaults);
        }

        if (object && config && typeof config === 'object') {
            var i, j, k;

            for (i in config) {
                object[i] = config[i];
            }

            if (enumerables) {
                for (j = enumerables.length; j--;) {
                    k = enumerables[j];
                    if (config.hasOwnProperty(k)) {
                        object[k] = config[k];
                    }
                }
            }
        }

        return object;
    };

applyIf

객체에 없는 속성들만(undefined) 복사한다.

        /**
         * Copies all the properties of config to object if they don't already exist.
         * @param {Object} object The receiver of the properties
         * @param {Object} config The source of the properties
         * @return {Object} returns obj
         */
        applyIf: function(object, config) {
            var property;

            if (object) {
                for (property in config) {
                    if (object[property] === undefined) {
                        object[property] = config[property];
                    }
                }
            }

            return object;
        },

bind

함수를 재정의하기 위해 사용된다.

부가적인 매개변수를 추가하는 등의 처리를 할 수 있으며, appendArgs의 값에 따라 args에 지정된 매개변수 처리를 다르게 할 수 있다.

  • true일 경우: 단순히 args에 지정된 매개변수를 뒤에 덧붙여서 호출한다.
  • 숫자일 경우: 숫자에 해당되는 위치에 args에 지정된 매개변수를 끼워넣어서 호출한다.
    /**
     * Create a new function from the provided `fn`, change `this` to the provided scope, optionally
     * overrides arguments for the call. (Defaults to the arguments passed by the caller)
     *
     * {@link Ext#bind Ext.bind} is alias for {@link Ext.Function#bind Ext.Function.bind}
     *
     * @param {Function} fn The function to delegate.
     * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
     * **If omitted, defaults to the default global environment object (usually the browser window).**
     * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
     * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
     * if a number the args are inserted at the specified position
     * @return {Function} The new function
     */
    bind: function(fn, scope, args, appendArgs) {
        if (arguments.length === 2) {
            return function() {
                return fn.apply(scope, arguments);
            };
        }

        var method = fn,
            slice = Array.prototype.slice;

        return function() {
            var callArgs = args || arguments;

            if (appendArgs === true) {
                callArgs = slice.call(arguments, 0);
                callArgs = callArgs.concat(args);
            }
            else if (typeof appendArgs == 'number') {
                callArgs = slice.call(arguments, 0); // copy arguments first
                Ext.Array.insert(callArgs, appendArgs, args);
            }

            return method.apply(scope || Ext.global, callArgs);
        };
    },

override

적용 대상에 따라 조금씩 다른 원리로 동작한다.

  • 적용 대상이 클래스인 경우: 적용 대상의 override()를 호출한다.
  • 적용 대상이 함수인 경우: 생성자로 가정되어 apply()를 통해서 프로토타입에 적용된다.
  • 적용 대상이 클래스의 인스턴스인 경우: 해당 인스턴스에 대해서만 오버라이딩되며, callParent() 호출을 통해 오버라이딩이 수행된다.
        /**
         * Overrides members of the specified `target` with the given values.
         * 
         * If the `target` is a class declared using {@link Ext#define Ext.define}, the
         * `override` method of that class is called (see {@link Ext.Base#override}) given
         * the `overrides`.
         *
         * If the `target` is a function, it is assumed to be a constructor and the contents
         * of `overrides` are applied to its `prototype` using {@link Ext#apply Ext.apply}.
         * 
         * If the `target` is an instance of a class declared using {@link Ext#define Ext.define},
         * the `overrides` are applied to only that instance. In this case, methods are
         * specially processed to allow them to use {@link Ext.Base#callParent}.
         * 
         *      var panel = new Ext.Panel({ ... });
         *      
         *      Ext.override(panel, {
         *          initComponent: function () {
         *              // extra processing...
         *              
         *              this.callParent();
         *          }
         *      });
         *
         * If the `target` is none of these, the `overrides` are applied to the `target`
         * using {@link Ext#apply Ext.apply}.
         *
         * Please refer to {@link Ext#define Ext.define} and {@link Ext.Base#override} for
         * further details.
         *
         * @param {Object} target The target to override.
         * @param {Object} overrides The properties to add or replace on `target`. 
         * @method override
         */
        override: function (target, overrides) {
            if (target.$isClass) {
                target.override(overrides);
            } else if (typeof target == 'function') {
                Ext.apply(target.prototype, overrides);
            } else {
                var owner = target.self,
                    name, value;

                if (owner && owner.$isClass) { // if (instance of Ext.define'd class)
                    for (name in overrides) {
                        if (overrides.hasOwnProperty(name)) {
                            value = overrides[name];

                            if (typeof value == 'function') {
                                //<debug>
                                if (owner.$className) {
                                    value.displayName = owner.$className + '#' + name;
                                }
                                //</debug>

                                value.$name = name;
                                value.$owner = owner;
                                value.$previous = target.hasOwnProperty(name)
                                    ? target[name] // already hooked, so call previous hook
                                    : callOverrideParent; // calls by name on prototype
                            }

                            target[name] = value;
                        }
                    }
                } else {
                    Ext.apply(target, overrides);
                }
            }

            return target;
        }
    });

바깥 고리