Ext JS 4: State Management

I recently updated an Ext JS 3 application to use Ext JS 4. It’s really more of a page than a full blown app, which uses Ext JS state management to remember values for some of the fields for browser forward/back navigation. Some things I experienced along the way…

1. Default state events
When you make a field, such as a text field, stateful, you get some default state events, whether or not you want them. A text field for example, adds the state events “resize” and “change”. I didn’t want those events. I only want to save state when a certain button is clicked. Getting rid of those state events proved to be a major pain. I wanted to add a “removeStateEvents” method to the Ext.state.Stateful mixin, but I could never get it added to the prototype before the mixin was added to the text field definition.

I ended up adding the method to “Ext.form.field.Base”. I figured this was okay because the Stateful mixin is inherited by this class and in my case, everything I wanted to save state on was form field. “removeStateEvents” is almost the same as Ext.state.Stateful.addStateEvents, except that it deletes the event name from the stateEventsByName object and removes the doSaveState listener for the given event.

Ext.form.field.Base.addMembers({
  removeStateEvents: function (events) {
    var me = this,
        i, event, stateEventsByName;

    if (me.stateful && me.getStateId()) {
      if (typeof events == 'string') {
        events = Array.prototype.slice.call(arguments, 0);
      }

      stateEventsByName = me.stateEventsByName || (me.stateEventsByName = {});

      for (i = events.length; i--; ) {
        event = events[i];

        if (stateEventsByName[event]) {
          delete stateEventsByName[event];
          me.un(event, me.onStateChange, me);
        }
      }
    }
  }
});

And then I called it on my text field in the initComponent of my extended container.

initComponent: function () {
    //...
    this.callParent(arguments);
    this.down('#searchText').removeStateEvents(['change','resize']);
}

This whole solution is annoying. I would prefer to just avoid adding the state events in the first place, but that would mean some aggressive overriding and therefore I ended up with the “remove before you can do anything” solution.

Seems like turning on stateful for a field should allow the field to specify exactly what events to listen for, instead of giving you some defaults (and potentially very frequently fired defaults at that). Something like this post on the remote storage provider extension, where defaultStateEvents can be configured would be nice.

2. Timing Matters
I could see that the state info I wanted for my fields was being saved correctly, but I had issues with the values setting on comboboxes. It turns out that value of the combobox was being initialized from the state manager prior to loading the stores for the comboboxes. So early that “isLoading” was false and instead the combobox went down the path of being set with an unknown value, and since those aren’t allowed for my comboboxes… well.

To solve this issue I moved some stuff around and loaded the combobox stores earlier, taking advantage of a store id and store manager. But then I also delayed the loading of my viewport a few milliseconds, to give the AJAX requests for the data stores a chance to kick off.

// Create data stores for the comboboxes, with autoLoad true like this:
Ext.create('Ext.data.Store', {
	storeId: 'comboboxStore',
	fields: ['label', 'value'],
	autoLoad: true,
	proxy: {
		type: 'ajax',
		url: 'resourc/url/goes/here',
		reader: {
			type: 'json',
			root: 'data'
		}
	}
});

Ext.state.Manager.setProvider(Ext.create('My.state.Provider'));
var stateReset = this.resetState();
// Delay the UI build a little bit, so the data stores get a chance to start loading
var task = new Ext.util.DelayedTask(function(stateReset){
	this.buildViewPort();
	if ( !stateReset ) {
		// Must be from a back button, run the find to pre-populate the grid
		this.doFindUsers();
	}
}, this, [stateReset]);
task.delay(100);

The Ext JS 3 version is not MVC and the code is already embedded in a legacy jsp page, so this was part of my “init” function that runs at Ext.onReady.

Version Info: Ext JS 4.1.1

Leave a comment