ExtJS 4: How to know when any field in a form (Ext.form.Panel) changes?

Clint Harris picture Clint Harris · Dec 19, 2011 · Viewed 28k times · Source

I'd like a single event listener that fires whenever any field in a form (i.e., Ext.form.Panel) changes. The Ext.form.Panel class doesn't fire an event for this itself, however.

What's the best way to listen for 'change' events for all fields in a form?

Answer

Clint Harris picture Clint Harris · Dec 19, 2011

Update: Added a 3rd option based on tip in comments (thanks @innerJL!)

Ok, looks like there are at least two fairly simple ways to do it.

Option 1) Add a 'change' listener to each field that is added to the form:

Ext.define('myapp.MyFormPanel', {
  extend: 'Ext.form.Panel',
  alias: 'myapp.MyFormPanel',
  ...

  handleFieldChanged: function() { 
    // Do something
  },

  listeners: {
    add: function(me, component, index) {
      if( component.isFormField ) {
        component.on('change', me.handleFieldChanged, me);
      }
    }
  }
});

This has at least one big drawback; if you "wrap" some fields in other containers and then add those containers to the form, it won't recognize the nested fields. In other words, it doesn't do a "deep" search through the component to see if it contains form field that need 'change' listeners.

Option 2) Use a component query to listen to all 'change' events fired from fields in a container.

Ext.define('myapp.MyController', {
  extend: 'Ext.app.Controller',
  ...

  init: function(application) {
    me.control({

        '[xtype="myapp.MyFormPanel"] field': {
            change: function() {
              // Do something
            }
        }
    });
  }
});

Option 3) Listen for the 'dirtychange' fired from the form panel's underlying 'basic' form (Ext.form.Basic). Important: You need to make sure you must enable 'trackResetOnLoad' by ensuring that {trackResetOnLoad:true} is passed to your form panel constructor.

Ext.define('myapp.MyFormPanel', {
  extend: 'Ext.form.Panel',
  alias: 'myapp.MyFormPanel',
  constructor: function(config) {
    config = config || {};
    config.trackResetOnLoad = true;
    me.callParent([config]);

    me.getForm().on('dirtychange', function(form, isDirty) {
       if( isDirty ) {
            // Unsaved changes exist
        }
        else {
            // NO unsaved changes exist
        }
    });
  }
});

This approach is the "smartest"; it allows you to know when the form has been modified, but also if the user modifies it back to it's original state. For example, if they change a text field from "Foo" to "Bar", the 'dirtychange' event will fire with 'true' for the isDirty param. But if the user then changes the field back to "Foo", the 'dirtychange' event will fire again and isDirty will be false.