forms - कस्टम ईएक्सटीजेएस फार्म फील्ड कॉम्पोनेन्ट कैसे बनाएं?




extjs extjs4 (6)

मैं इसमें अन्य ExtJS घटकों का उपयोग कर कस्टम ExtJS फॉर्म फ़ील्ड घटक बनाना चाहता हूं (उदाहरण के लिए TreePanel)। मैं इसे आसानी से कैसे कर सकता हूं?

मैंने Ext.form.field.Base दस्तावेज़ Ext.form.field.Base लेकिन मैं फ़ील्ड बॉडी को फील्ड fieldSubTpl द्वारा परिभाषित नहीं करना चाहता fieldSubTpl । मैं सिर्फ कोड लिखना चाहता हूं जो ExtJS घटकों को बनाता है और शायद कुछ अन्य कोड जो मान प्राप्त करता है और सेट करता है।

अद्यतन: सारांशित उद्देश्य अनुवर्ती हैं:

  • यह नया घटक फॉर्म जीयूआई में फ़ील्ड के रूप में फिट होना चाहिए। इसमें लेबल और उसी संरेखण (लेबल, एंकर) को अन्य क्षेत्रों के आगे हैकिंग की आवश्यकता के बिना होना चाहिए।

  • संभवतः, मुझे कुछ getValue, setValue तर्क लिखना है। मैं इसे अलग-अलग कोड बनाने की बजाय इस घटक में एम्बेड करना चाहता हूं जो चीजों को और छिपे हुए फॉर्म फ़ील्ड में कॉपी करता है जिन्हें मुझे प्रबंधित करना भी है।


@RobAgar के उत्तर को विस्तारित करने के लिए, एक बहुत ही सरल दिनांक समय फ़ील्ड के बाद मैंने एक्स्टजेएस 3 के लिए लिखा था और यह एक्स्टजेस 4 के लिए बनाया गया क्विकपोर्ट है। महत्वपूर्ण बात Ext.form.field.Field mixin का उपयोग है। यह मिश्रण तार्किक व्यवहार और फॉर्म फ़ील्ड की स्थिति के लिए एक सामान्य इंटरफ़ेस प्रदान करता है, जिसमें निम्न शामिल हैं:

क्षेत्र मूल्यों के लिए गेटटर और सेटर विधियां मूल्य और वैधता परिवर्तनों को ट्रैक करने के लिए ईवेंट और विधियां सत्यापन को ट्रिगर करने के तरीके

इसका उपयोग कई क्षेत्रों के संयोजन के लिए किया जा सकता है और उन्हें एक के रूप में कार्य करने दें। कुल कस्टम Ext.form.field.Base लिए मैं Ext.form.field.Base का विस्तार करने की अनुशंसा करता हूं

यहां उदाहरण दिया गया है कि मैंने उपर्युक्त उल्लेख किया है। यह जूता होना चाहिए कि डेट ऑब्जेक्ट की तरह कुछ भी करने के लिए यह कितना आसान हो सकता है जहां हमें गेटर और सेटर के भीतर डेटा को प्रारूपित करने की आवश्यकता होती है।

Ext.define('QWA.form.field.DateTime', {
    extend: 'Ext.form.FieldContainer',
    mixins: {
        field: 'Ext.form.field.Field'
    },
    alias: 'widget.datetimefield',
    layout: 'hbox',
    width: 200,
    height: 22,
    combineErrors: true,
    msgTarget: 'side',
    submitFormat: 'c',

    dateCfg: null,
    timeCfg: null,

    initComponent: function () {
        var me = this;
        if (!me.dateCfg) me.dateCfg = {};
        if (!me.timeCfg) me.timeCfg = {};
        me.buildField();
        me.callParent();
        me.dateField = me.down('datefield')
        me.timeField = me.down('timefield')

        me.initField();
    },

    //@private
    buildField: function () {
        var me = this;
        me.items = [
        Ext.apply({
            xtype: 'datefield',
            submitValue: false,
            format: 'd.m.Y',
            width: 100,
            flex: 2
        }, me.dateCfg),
        Ext.apply({
            xtype: 'timefield',
            submitValue: false,
            format: 'H:i',
            width: 80,
            flex: 1
        }, me.timeCfg)]
    },

    getValue: function () {
        var me = this,
            value,
            date = me.dateField.getSubmitValue(),
            dateFormat = me.dateField.format,
            time = me.timeField.getSubmitValue(),
            timeFormat = me.timeField.format;
        if (date) {
            if (time) {
                value = Ext.Date.parse(date + ' ' + time, me.getFormat());
            } else {
                value = me.dateField.getValue();
            }
        }
        return value;
    },

    setValue: function (value) {
        var me = this;
        me.dateField.setValue(value);
        me.timeField.setValue(value);
    },

    getSubmitData: function () {
        var me = this,
            data = null;
        if (!me.disabled && me.submitValue && !me.isFileUpload()) {
            data = {},
            value = me.getValue(),
            data[me.getName()] = '' + value ? Ext.Date.format(value, me.submitFormat) : null;
        }
        return data;
    },

    getFormat: function () {
        var me = this;
        return (me.dateField.submitFormat || me.dateField.format) + " " + (me.timeField.submitFormat || me.timeField.format)
    }
});

अब यह अच्छा है। दूसरे दिन, मैंने महसूस किया कि मैं ऑफ-विषय था, इससे पहले कि मैंने एक और सवाल का जवाब देने के लिए एक पहेली तैयार की। और यहां आप हैं, आखिर में मेरे जवाब पर सवाल ध्यान में ला रहे हैं। धन्यवाद!

तो, किसी अन्य घटक से कस्टम फ़ील्ड को लागू करने के लिए आवश्यक कदम यहां दिए गए हैं:

  1. बाल घटक बनाना
  2. बाल घटक प्रस्तुत करें
  3. बच्चे के घटक को सुनिश्चित करना आकार और सही ढंग से आकार बदलता है
  4. मूल्य प्राप्त करना और सेट करना
  5. घटनाओं को रिलेइंग

बाल घटक बनाना

घटक बनाते हुए पहला भाग आसान है। किसी अन्य उपयोग के लिए घटक बनाने की तुलना में विशेष रूप से कुछ भी नहीं है।

हालांकि, आपको बच्चे को मूल क्षेत्र की initComponent विधि में बनाना होगा (और समय प्रस्तुत करने पर नहीं)। ऐसा इसलिए है क्योंकि बाहरी कोड वैध रूप से उम्मीद कर सकता है कि किसी घटक की सभी निर्भर वस्तुओं को initComponent (उदाहरण के लिए श्रोताओं को जोड़ने के लिए) के बाद तत्काल किया जाता है।

इसके अलावा, आप अपने आप को दयालु हो सकते हैं और सुपर विधि को कॉल करने से पहले बच्चे को बना सकते हैं। यदि आप सुपर विधि के बाद बच्चे को बनाते हैं, तो आप उस समय अपने बच्चे के setValue विधि ( setValue देखें) पर कॉल कर सकते हैं जब बच्चा अभी तक तत्काल नहीं है।

initComponent: function() {
    this.childComponent = Ext.create(...);
    this.callParent(arguments);
}

जैसा कि आप देखते हैं, मैं एक घटक बना रहा हूं, जो आप ज्यादातर मामलों में चाहते हैं। लेकिन आप फैंसी जाना और कई बाल घटकों को लिखना भी चाह सकते हैं। इस मामले में, मुझे लगता है कि यह जितनी जल्दी हो सके प्रसिद्ध क्षेत्रों में वापस चतुर होगा: यानी, एक घटक को बाल घटक के रूप में बनाएं, और इसमें लिखें।

प्रतिपादन

फिर प्रतिपादन का सवाल आता है। सबसे पहले मैंने कंटेनर div प्रस्तुत करने के लिए fieldSubTpl का उपयोग करने पर विचार किया, और इसमें बाल घटक स्वयं प्रस्तुत करता है। हालांकि, हमें उस मामले में टेम्पलेट सुविधाओं की आवश्यकता नहीं है, इसलिए हम getSubTplMarkup विधि का उपयोग करके इसे पूरी तरह से बाईपास कर सकते हैं।

मैंने यह देखने के लिए एक्स में अन्य घटकों की खोज की कि वे बाल घटकों के प्रतिपादन को कैसे प्रबंधित करते हैं। मुझे बाउंडलिस्ट और इसकी पेजिंग टूलबार में एक अच्छा उदाहरण मिला ( code देखें)। इसलिए, बच्चे के घटक के मार्कअप को प्राप्त करने के लिए, हम बच्चे की getRenderTree विधि के साथ संयोजन में Ext.DomHelper.generateMarkup का उपयोग कर सकते हैं।

तो, यहां हमारे क्षेत्र के लिए getSubTplMarkup का कार्यान्वयन है:

getSubTplMarkup: function() {
    // generateMarkup will append to the passed empty array and return it
    var buffer = Ext.DomHelper.generateMarkup(this.childComponent.getRenderTree(), []);
    // but we want to return a single string
    return buffer.join('');
}

अब, यह पर्याप्त नहीं है। बाउंडलिस्ट का कोड हमें सीखता है कि घटक प्रतिपादन में एक और महत्वपूर्ण हिस्सा है: बच्चे घटक के finishRender() विधि को कॉल करना। सौभाग्य से, हमारे कस्टम क्षेत्र में अपनी खुद की finishRenderChildren होगी, finishRenderChildren विधि जिसे केवल तब किया जाना चाहिए जब उसे करने की आवश्यकता हो।

finishRenderChildren: function() {
    this.callParent(arguments);
    this.childComponent.finishRender();
}

पुन: आकार देने

अब हमारे बच्चे को सही जगह पर प्रस्तुत किया जाएगा, लेकिन यह अपने मूल क्षेत्र के आकार का सम्मान नहीं करेगा। यह विशेष रूप से एक फार्म फ़ील्ड के मामले में परेशान है, क्योंकि इसका मतलब है कि यह एंकर लेआउट का सम्मान नहीं करेगा।

यह ठीक करने के लिए बहुत सरल है, जब माता-पिता के क्षेत्र का आकार बदलता है तो हमें केवल बच्चे का आकार बदलने की आवश्यकता होती है। मेरे अनुभव से, यह कुछ ऐसा है जो Ext3 के बाद से काफी सुधार हुआ था। यहां, हमें केवल लेबल के लिए अतिरिक्त स्थान नहीं भूलना होगा:

onResize: function(w, h) {
    this.callParent(arguments);
    this.childComponent.setSize(w - this.getLabelWidth(), h);
}

हैंडलिंग मूल्य

यह हिस्सा निश्चित रूप से आपके बच्चे के घटक, और आपके द्वारा बनाए जा रहे क्षेत्र पर निर्भर करेगा। इसके अलावा, अब से, यह नियमित रूप से आपके बच्चे के घटकों का उपयोग करने की बात है, इसलिए मैं इस भाग को बहुत अधिक विस्तारित नहीं करूंगा।

एक मिनीमा, आपको अपने क्षेत्र के setValue और setValue विधियों को भी लागू करने की आवश्यकता है। इससे प्रपत्र कार्य की getFieldValues विधि getFieldValues जाएगी, और यह फ़ॉर्म से रिकॉर्ड्स लोड / अपडेट करने के लिए पर्याप्त होगा।

सत्यापन को संभालने के लिए, आपको getErrors को लागू करना होगा। इस पहलू को पॉलिश करने के लिए, आप अपने क्षेत्र की अमान्य स्थिति को दृष्टि से प्रतिनिधित्व करने के लिए कुछ सीएसएस नियम जोड़ना चाह सकते हैं।

फिर, यदि आप चाहते हैं कि आपका क्षेत्र किसी ऐसे रूप में उपयोग योग्य हो जो वास्तविक रूप के रूप में सबमिट किया जाएगा (जैसा कि AJAX अनुरोध के विपरीत है), आपको एक मूल्य वापस करने के लिए getSubmitValue आवश्यकता होगी getSubmitValue बिना किसी नुकसान के स्ट्रिंग में getSubmitValue जा सके।

इसके अलावा, जहां तक ​​मुझे पता है, आपको Ext.form.field.Base द्वारा पेश की गई अवधारणा या कच्चे मूल्य के बारे में चिंता करने की आवश्यकता नहीं है क्योंकि इसका उपयोग केवल वास्तविक इनपुट तत्व में मान के प्रतिनिधित्व को संभालने के लिए किया जाता है। इनपुट के रूप में हमारे एक्स घटक के साथ, हम उस सड़क से दूर हैं!

आयोजन

आपका आखिरी काम आपके क्षेत्रों के लिए कार्यक्रमों को लागू करना होगा। आप शायद Ext.form.field.Field की तीन घटनाओं को आग लगाना चाहते हैं, जो change , dirtychange change और validitychange change

फिर, कार्यान्वयन आपके द्वारा उपयोग किए जाने वाले बाल घटक के लिए बहुत विशिष्ट होगा और ईमानदार होने के लिए, मैंने इस पहलू को बहुत अधिक नहीं खोजा है। तो मैं आपको अपने लिए यह तार दूंगा।

हालांकि, मेरा प्रारंभिक निष्कर्ष यह है कि Ext.form.field.Field लिए सभी भारी उठाने की पेशकश करता है, बशर्ते कि (1) आवश्यकता होने पर आप checkChange कॉल करें, और (2) है isEqual कार्यान्वयन आपके क्षेत्र के मूल्य प्रारूप के साथ काम कर रहा है।

उदाहरण: TODO सूची फ़ील्ड

अंत में, एक TODO सूची फ़ील्ड का प्रतिनिधित्व करने के लिए ग्रिड का उपयोग करके, यहां एक पूर्ण कोड उदाहरण है।

आप इसे jsFiddle पर लाइव देख सकते हैं, जहां मैं यह दिखाने की कोशिश करता हूं कि यह क्षेत्र व्यवस्थित ढंग से व्यवहार करता है।

Ext.define('My.form.field.TodoList', {
    // Extend from Ext.form.field.Base for all the label related business
    extend: 'Ext.form.field.Base'

    ,alias: 'widget.todolist'

    // --- Child component creation ---

    ,initComponent: function() {

        // Create the component

        // This is better to do it here in initComponent, because it is a legitimate 
        // expectationfor external code that all dependant objects are created after 
        // initComponent (to add listeners, etc.)

        // I will use this.grid for semantical access (value), and this.childComponent
        // for generic issues (rendering)
        this.grid = this.childComponent = Ext.create('Ext.grid.Panel', {
            hideHeaders: true
            ,columns: [{dataIndex: 'value', flex: 1}]
            ,store: {
                fields: ['value']
                ,data: []
            }
            ,height: this.height || 150
            ,width: this.width || 150

            ,tbar: [{
                text: 'Add'
                ,scope: this
                ,handler: function() {
                    var value = prompt("Value?");
                    if (value !== null) {
                        this.grid.getStore().add({value: value});
                    }
                }
            },{
                text: "Remove"
                ,itemId: 'removeButton'
                ,disabled: true // initial state
                ,scope: this
                ,handler: function() {
                    var grid = this.grid,
                        selModel = grid.getSelectionModel(),
                        store = grid.getStore();
                    store.remove(selModel.getSelection());
                }
            }]

            ,listeners: {
                scope: this
                ,selectionchange: function(selModel, selection) {
                    var removeButton = this.grid.down('#removeButton');
                    removeButton.setDisabled(Ext.isEmpty(selection));
                }
            }
        });

        // field events
        this.grid.store.on({
            scope: this
            ,datachanged: this.checkChange
        });

        this.callParent(arguments);
    }

    // --- Rendering ---

    // Generates the child component markup and let Ext.form.field.Base handle the rest
    ,getSubTplMarkup: function() {
        // generateMarkup will append to the passed empty array and return it
        var buffer = Ext.DomHelper.generateMarkup(this.childComponent.getRenderTree(), []);
        // but we want to return a single string
        return buffer.join('');
    }

    // Regular containers implements this method to call finishRender for each of their
    // child, and we need to do the same for the component to display smoothly
    ,finishRenderChildren: function() {
        this.callParent(arguments);
        this.childComponent.finishRender();
    }

    // --- Resizing ---

    // This is important for layout notably
    ,onResize: function(w, h) {
        this.callParent(arguments);
        this.childComponent.setSize(w - this.getLabelWidth(), h);
    }

    // --- Value handling ---

    // This part will be specific to your component of course

    ,setValue: function(values) {
        var data = [];
        if (values) {
            Ext.each(values, function(value) {
                data.push({value: value});
            });
        }
        this.grid.getStore().loadData(data);
    }

    ,getValue: function() {
        var data = [];
        this.grid.getStore().each(function(record) {
            data.push(record.get('value'));
        });
        return data;        
    }

    ,getSubmitValue: function() {
        return this.getValue().join(',');
    }
});

क्या आप यूआई आवश्यकताओं का वर्णन कर सकते हैं जिनके पास आपके पास कुछ और है? क्या आप सुनिश्चित हैं कि आपको TreePanel का समर्थन करने के लिए एक संपूर्ण क्षेत्र बनाने की भी आवश्यकता है? एक सामान्य पेड़ पैनल पर एक क्लिक हैंडलर से छिपे हुए फ़ील्ड का मान क्यों सेट न करें (एपीआई में "छुपा" xtype देखें)?

अपने प्रश्न का पूरी तरह उत्तर देने के लिए, आप ExtJS घटकों को विस्तारित करने के तरीके पर कई ट्यूटोरियल पा सकते हैं। आप Ext.override () या Ext.Extend () विधियों का लाभ उठाकर ऐसा करते हैं।

लेकिन मेरी भावना यह है कि आप अपने डिजाइन को अधिक जटिल बना सकते हैं। इस छिपे हुए क्षेत्र में मूल्य निर्धारित करके आप जो करना चाहते हैं उसे प्राप्त कर सकते हैं। यदि आपके पास जटिल डेटा है, तो आप मान को कुछ XML या JSON स्ट्रिंग के रूप में सेट कर सकते हैं।

संपादित करें यहां कुछ ट्यूटोरियल हैं। जब आप अपने यूआई डिज़ाइन की बात करते हैं तो मैं अत्यधिक KISS नियम के साथ जाने की अनुशंसा करता हूं। मूर्ख इसे सहज ही रखो! पैनलों का उपयोग कर घटकों का विस्तार


चूंकि सवाल पूछा गया था अस्पष्ट - मैं केवल ExtJS v4 के लिए मूल पैटर्न प्रदान कर सकता हूं।

यहां तक ​​कि यदि यह बहुत विशिष्ट नहीं है, तो यह अग्रिम है कि यह इस तरह सार्वभौमिक है:

Ext.define('app.view.form.field.CustomField', {
    extend: 'Ext.form.field.Base',
    requires: [
        /* require further components */
    ],

    /* custom configs & callbacks */

    getValue: function(v){
        /* override function getValue() */
    },

    setValue: function(v){
        /* override function setValue() */
    },

    getSubTplData: [
       /* most likely needs to be overridden */
    ],

    initComponent: function(){

        /* further code on event initComponent */

        this.callParent(arguments);
    }
});

फ़ाइल /ext/src/form/field/Base.js उन सभी कॉन्फ़िगरेशन और फ़ंक्शंस के नाम प्रदान करती है जिन्हें ओवरराइड किया जा सकता है।


यहां एक कस्टम पैनल का एक उदाहरण दिया गया है जो एक एक्सटी पैनल बढ़ाता है। आप किसी भी घटक का विस्तार कर सकते हैं, फ़ील्ड, विधियों और घटनाओं के लिए दस्तावेज़ों को चेक कर सकते हैं जिनके साथ आप खेल सकते हैं।

Ext.ns('yournamespace');

yournamespace.MyPanel = function(config) {
    yournamespace.MyPanel.superclass.constructor.call(this, config);
} 

Ext.extend(yournamespace.MyPanel, Ext.Panel, {

    myGlobalVariable : undefined,

    constructor : function(config) {
        yournamespace.MyPanel.superclass.constructor.apply(this, config);
    },

    initComponent : function() {
        this.comboBox = new Ext.form.ComboBox({
            fieldLabel: "MyCombo",
            store: someStore,
            displayField:'My Label',
            typeAhead: true,
            mode: 'local',
            forceSelection: true,
            triggerAction: 'all',
            emptyText:'',
            selectOnFocus:true,
            tabIndex: 1,
            width: 200
        });

        // configure the grid
        Ext.apply(this, {
            listeners: {
                'activate': function(p) {
                    p.doLayout();
                 },
                 single:true
            },

            xtype:"form",
            border: false,
            layout:"absolute",
            labelAlign:"top",
            bodyStyle:"padding: 15px",
            width: 350,
            height: 75,

            items:[{
                xtype:"panel",
                layout:"form",
                x:"10",
                y:"10",
                labelAlign:"top",
                border:false,
                items:[this.comboBox]
            },
            {
                xtype:"panel",
                layout:"form",
                x:"230",
                y:"26",
                labelAlign:"top",
                border:false,
                items:[{
                    xtype:'button',
                    handler: this.someAction.createDelegate(this),
                    text: 'Some Action'
                }]
            }]
        }); // eo apply

        yournamespace.MyPanel.superclass.initComponent.apply(this, arguments);

        this.comboBox.on('select', function(combo, record, index) {
            this.myGlobalVariable = record.get("something");
        }, this);

    }, // eo function initComponent

    someAction : function() {
        //do something
    },

    getMyGlobalVariable : function() {
        return this.myGlobalVariable;
    }

}); // eo extend

Ext.reg('mypanel', yournamespace.MyPanel);

हे। Ext.form.FieldContainer पोस्ट करने के बाद मुझे पता चला कि Ext.form.FieldContainer केवल एक फ़ील्ड कंटेनर नहीं है, बल्कि एक पूरी तरह से घटक घटक कंटेनर है, इसलिए एक सरल समाधान है।

आपको केवल FieldContainer का विस्तार करना है, बच्चे के घटकों को जोड़ने के लिए initComponent ओवरराइड initComponent , और setValue , setValue और सत्यापन मान विधियों को अपने मूल्य डेटा प्रकार के लिए उपयुक्त लागू setValue

यहां एक ग्रिड के साथ एक उदाहरण दिया गया है जिसका मूल्य नाम / मूल्य जोड़ी वस्तुओं की एक सूची है:

Ext.define('MyApp.widget.MyGridField', {
  extend: 'Ext.form.FieldContainer',
  alias: 'widget.mygridfield',

  layout: 'fit',

  initComponent: function()
  {
    this.callParent(arguments);

    this.valueGrid = Ext.widget({
      xtype: 'grid',
      store: Ext.create('Ext.data.JsonStore', {
        fields: ['name', 'value'],
        data: this.value
      }),
      columns: [
        {
          text: 'Name',
          dataIndex: 'name',
          flex: 3
        },
        {
          text: 'Value',
          dataIndex: 'value',
          flex: 1
        }
      ]
    });

    this.add(this.valueGrid);
  },

  setValue: function(value)
  {
    this.valueGrid.getStore().loadData(value);
  },

  getValue: function()
  {
    // left as an exercise for the reader :P
  }
});




extjs4