How to create custom ExtJS form field component?

ext form combobox
extjs form field checkbox
extjs form validation
extjs textfield
extjs component
extjs login form example
extjs textarea
extjs xtype label

I want to create custom ExtJS form field components using other ExtJS components in it (e.g. TreePanel). How can I do it most easily?

I've read docs of Ext.form.field.Base but I don't want to define field body by fieldSubTpl. I just want to write code which creates ExtJS components and maybe some other code which gets and sets values.

Update: Summarized purposes are the followings:

  • This new component should fit in the form GUI as a field. It should have label and the same alignment (label, anchor) of other fields without need of further hacking.

  • Possibly, I have to write some getValue, setValue logic. I'd rather embed it into this component than making separated code which copies things into further hidden form fields that I also have to manage.

To extend @RobAgar 's answer, following a really simple Date Time field that I wrote for ExtJS 3 and it's quickport that I made for ExtJS 4. The important thing is the use of the Ext.form.field.Field mixin. This mixin provides a common interface for the logical behavior and state of form fields, including:

Getter and setter methods for field values Events and methods for tracking value and validity changes Methods for triggering validation

This can be used for combining multiple fields and let act them as one. For a total custom fieldtype I recommend to extend Ext.form.field.Base

Here is the example that I mentioned above. It should shoe how easy this can be done even for something like a date object where we need to format the data within the getter and setter.

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

Ext.form.field.Text, Is it a correct way to create custom form fields or not? For this moment component acts not like native form item, because. Ext.form.Panel  I want to create custom ExtJS form field components using other ExtJS components in it (e.g. TreePanel). How can I do it most easily? I've read docs of Ext.form.field.Base but I don't want to define field body by fieldSubTpl. I just want to write code which creates ExtJS components and maybe some other code which gets and sets values.

Now that's cool. The other day, I created a fiddle to answer another question before realizing I was off-topic. And here your are, finally bringing to my attention the question to my answer. Thanks!

So, here are the steps required in implementing a custom field from another component:

  1. Creating the child component
  2. Render the child component
  3. Ensuring the child component is sized and resized correctly
  4. Getting and setting value
  5. Relaying events
Creating the child component

The first part, creating the component, is easy. There's nothing particular compared to creating a component for any other usage.

However, you must create the child in the parent field's initComponent method (and not at rendering time). This is because external code can legitimately expect that all dependent objects of a component are instantiated after initComponent (e.g. to add listeners to them).

Furthermore, you can be kind to yourself and create the child before calling the super method. If you create the child after the super method, you may get a call to your field's setValue method (see bellow) at a time when the child is not yet instantiated.

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

As you see, I am creating a single component, which is what you'll want in most case. But you can also want to go fancy and compose multiple child components. In this case, I think it would be clever to back to well known territories as quickly as possible: that is, create one container as the child component, and compose in it.

Rendering

Then comes the question of rendering. At first I considered using fieldSubTpl to render a container div, and have the child component render itself in it. However, we don't need the template features in that case, so we can as well bypass it completely using the getSubTplMarkup method.

I explored other components in Ext to see how they manage the rendering of child components. I found a good example in BoundList and its paging toolbar (see the code). So, in order to obtain the child component's markup, we can use Ext.DomHelper.generateMarkup in combination with the child's getRenderTree method.

So, here's the implementation of getSubTplMarkup for our field:

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('');
}

Now, that's not enough. The code of BoundList learns us that there's another important part in component rendering: calling the finishRender() method of the child component. Fortunately, our custom field will have its own finishRenderChildren method called just when that needs to be done.

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

Now our child will be rendered in the right place, but it will not respect its parent field size. That is especially annoying in the case of a form field, because that means it won't honor the anchor layout.

That's very straightforward to fix, we just need to resize the child when the parent field is resized. From my experience, this is something that was greatly improved since Ext3. Here, we just need to not forget the extra space for the label:

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

This part will, of course, depend on your child component(s), and the field you're creating. Moreover, from now on, it's just a matter of using your child components in a regular way, so I won't detail this part too much.

A minima, you also need to implement the getValue and setValue methods of your field. That will make the getFieldValues method of the form work, and that will be enough to load/update records from the form.

To handle validation, you must implement getErrors. To polish this aspect, you may want to add a handful of CSS rules to visually represent the invalid state of your field.

Then, if you want your field to be usable in a form that will be submitted as an actual form (as opposed to with an AJAX request), you'll need getSubmitValue to return a value that can be casted to a string without damage.

Apart from that, as far as I know, you don't have to worry about the concept or raw value introduced by Ext.form.field.Base since that's only used to handle the representation of the value in an actual input element. With our Ext component as input, we're way off that road!

Events

Your last job will be to implement the events for your fields. You will probably want to fire the three events of Ext.form.field.Field, that is change, dirtychange and validitychange.

Again, the implementation will be very specific to the child component you use and, to be honest, I haven't explored this aspect too much. So I'll let you wire this for yourself.

My preliminary conclusion though, is that Ext.form.field.Field offers to do all the heavy lifting for you, provided that (1) you call checkChange when needed, and (2) isEqual implementation is working with your field's value format.

Example: TODO list field

Finally, here's a complete code example, using a grid to represent a TODO list field.

You can see it live on jsFiddle, where I tries to show that the field behaves in an orderly manner.

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(',');
    }
});

Ext.form.field.Field, All Components are subclasses of the Ext.Component class which allows This example uses Ext.create() to instantiate two Panels, then adds those Panels as child Components of a Viewport: This example creates a new class, My.custom​. UI Components (Usually Containers, or form Fields) in a configured layout,  Sencha ExtJS - How to dynamically create a form textfield (Ext.form.field.Text) Without much introduction, here’s a large block of Sencha ExtJS controller code. The code needs to be cleaned up, but at the moment it shows: How to dynamically create a Sencha ExtJS form textfield; How to dynamically create a Sencha ExtJS form checkboxgroup

Heh. After posting the bounty I found out that Ext.form.FieldContainer isn't just a field container, but a fully fledged component container, so there is a simple solution.

All you need to do is extend FieldContainer, overriding initComponent to add the child components, and implement setValue, getValue and the validation methods as appropriate for your value data type.

Here's an example with a grid whose value is a list of name/value pair objects:

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

Forms, custom fields, it is most likely that you will want to extend the Ext.form.field. You will also need to make sure that initField is called during the component's  See the Creating new UI controls chapter in Component Guide for details on how and to either extend or augment Ext JS base classes to create custom Components. The Ext.Component class by itself. Usually one doesn't need to instantiate the Ext.Component class. There are subclasses which implement specialized use cases, covering most application

Creating custom form field, A tag name or DomHelper spec used to create the Element which will encapsulate this Component. Ext JS provides a wide range of useful Components out of the box, and any Component can easily be extended to create a customized Component. The Component Hierarchy. A Container is a special type of Component that can contain other Components. A typical application is made up of many nested Components in a tree-like structure that is referred

Since the question was asked rather vague - I only can provide the basic pattern for ExtJS v4.

Even if it's not too specific, it has the advance that it's rather universal like this:

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

The file /ext/src/form/field/Base.js provides the names of all configs and functions that can be overridden.

Components, The maximum value in pixels which this BoxComponent will set its height to. A custom CSS class to apply to the field's underlying element (defaults to ''). This is only processed by form fields (e.g., Ext.form.field.Text) at present, but this setting is inherited and so can be set on a parent container. When set to true by a component or not set by a component but inherited from an ancestor container, Ext.data.Validation records are used to automatically bind validation results for any form field

Ext.form.field.Field - Ext JS, In the example above the composite's fieldLabel will be set to 'Title, First, Last' as A custom CSS class to apply to the field's underlying element (defaults to ''). Hello! I'm trying to create custom form field which is consists from 2 standart form fields. And I want it to act like a usual form field. Maybe someone could help me by giving several tips or even write a simple tutorial about custom form fields, which I believe will be helpful for a lot of beginners?

Ext.form.field.TextArea - Ext JS 4.2.0, NOTE: When implementing custom fields, it is most likely that you will want to extend the Ext.form.field.Base component class rather than using this mixin directly, as BaseField contains additional logic for generating an actual DOM complete with Ext.form.Labelable display and a form input field, plus methods that bind the Field value getters

Ext.form.TextField - Ext JS 3.4, Ext JS Api - API documentation from Sencha. Forms. A Form Panel is nothing more than a basic Panel with form handling abilities added. Form Panels can be used throughout an Ext application wherever there is a need to collect data from the user.

Comments
  • You might want to look at the ItemSelector extension which does exactly that.
  • Similar question is answered here: stackoverflow.com/questions/6092112/…
  • what do you want it to do other than being a form field?
  • Hi Rob I think you forgot a important thing; the Ext.form.field.Field mixin. I added a answer with a example how to use it. Much the same like yours but it was easier to post it then to add all to comments of your answer.
  • They're not easy in Ext 4 - that's why he's asking for an EXT 4 Example :D
  • Not sure if the question was tagged later with Ext4, but in that version, it should be as easy as using the Ext.form.field.Field mixin, and overriding your getValue and setValue. Although I haven't had to do this yet, but I know that is the idea.
  • This doesn't really qualify as a custom form field - it's just a combo box with a set of defined defaults applied.
  • Yet another Ext 3 example when the OP asked for Ext 4.
  • I wrote a new part to the question.
  • I have expressed requirement for custom fields so I can't ignore it. Maybe this is complicated but what I want to do is to push the complicated parts into the lower levels of the application by making a new component and keeping the top levels simple by using this new component as an xtype or sg like this. Has it sense? I am searching for a good tutorial but I haven't found the ultimate one.
  • Again, this is an Ext 3 example, not Ext 4. There are massive differenced in the code syntax for the two versions of the framework.