ExtJS

Ext JS 4: A File Browser with Spring 3 Server-side

I recently needed to create a file browser to access log files for my application. I don’t have access to the machines where the log files reside, so by creating a file browsing application I could ensure access controls and still get to the logs. From within my file browser I can open the log file in another browser tab/window or download the file. This was also my first time working with trees…

Here is what it looks like:
FileBrowser
(more…)

Ext JS 4: Two Ways to Configure Selection Model in Grid

There are two ways to configure the selection model on the grid. The two configuration options are selModel and selType. Below is an example of using both. Note, the configurations that I put in are just for demonstrations purposes, not necessarily recommended.

Example 1 – selType
This only uses configuration to set up the selection model. If using all the default configs for the selection model, the configuration options in selModel are not needed.

{
	xtype: 'grid',
	title: 'My Grid',
	store: myStore //configured elsewhere
	selType: 'checkboxmodel',
	selModel: {
		checkOnly: true,
		injectCheckbox: 1
	}
	columns: [
		// configure columns here
	]
}

Example 2 – selModel
This creates the selection model and only uses selModel.

{
	xtype: 'grid',
	title: 'My Grid',
	store: myStore //configured elsewhere
	selModel: Ext.create('Ext.selection.CheckboxModel, {
		checkOnly: true,
		injectCheckbox: 1
	}),
	columns: [
		// configure columns here
	]
}

Versions: Ext JS 4.1.1

Ext JS 4: Loading Reference Data Prior to Starting Application

I have recently found that I either need to load reference data or meta data to support role-based navigation before building the viewport.

In the first example below, I’m populating a variety of data stores prior to actually loading my application. This example does not use the MVC pattern.

In the second example, I’m loading toolbar configurations and other meta-data such as titles that are role-based before loading my application.

Loading Reference Data – Example 1

Ext.define('MyApp', {

	requires: [
		'Ext.Viewport',
	],

    launch: function() {

		this.storeOne = Ext.create('Ext.data.Store', {
			storeId: 'storeOne',
			model: 'MyApp.MyModel',
			autoLoad: false
		});
		
		this.storeTwo = Ext.create('Ext.data.Store', {
			storeId: 'storeTwo',
			model: 'MyApp.AnotherModel',
			autoLoad: false
		});
		
		Ext.Ajax.request({
			url: './url/for/refdata',
			jsonData: storeList,
			scope: this,
			success: this.onRefDataLoaded,
			failure: this.onRefDataLoadError
		});
	},

	onRefDataLoaded : function (response) {
		var json = Ext.decode(response.responseText), msg, item,
			success = false;

		if (typeof json.success !== "undefined" && json.success === false) {
			msg = json.message;
		} else {
			try {
				this.storeOne.loadRawData(json.storeOneData);
				this.storeTwo.loadRawData(json.storeTwoData);
				success = true;
			} catch (err) {
				msg = "An unexpected error occurred.";
			}
		}

		if (success) {
			item = {
				// put the application config stuff here.
				// i have a panel with a form on it
			};
		} else {
			// build a panel that will show the error message
			item = {
				xtype: 'panel',
				autoScroll: true,
				html: msg,
				bodyPadding: 10
			};
		}

		this.mainView = Ext.create('Ext.container.Viewport', {
            layout: 'border',
            items : [
				{
					id: 'center-container',
					xtype: 'container',
					region: 'center',
					layout: 'fit',
					items: [item]
				}
			]
		});
	},

	// Couldn't properly load the reference data, the AJAX request failed
	// Build the viewport with the error message
	onRefDataLoadError : function () {
		this.mainView = Ext.create('Ext.container.Viewport', {
			layout: 'border',
			items : [
				{
					id: 'center-container',
					xtype: 'container',
					region: 'center',
					layout: 'fit',
					items: {
						xtype: 'panel',
						autoScroll: true,
						html: "Something went wrong",
						bodyPadding: 10
					}
				}
			]
		});
	}
});

The JSON response for the ref data load looks like this:

{
	"storeOneData" : {
		"success" : true,"rows" : [
			{"id":1, "type":"UserType1"},
			{"id":2, "type":"UserType2"}
		]
	},
	"storeTwoData" : {
		"success" : true,"rows" : [
			{"id":1, "genre":"Jazz"},
			{"id":2, "genre":"Rock"},
	        {"id":3, "genre":"Pop"},
	        {"id":4, "genre":"Hip Hop"}
		]
	},
}

Loading Navigation Configurations – Example 2
In this example, instead of populating data stores, I’m querying the server to get configs for a toolbar and other data for the specific user. This example is using the MVC pattern, but dynamically loading the the controllers, following the pattern described here: An ExtJS MVC application with dynamically loaded views and controllers. The interesting part are the launch and onLoadNavButtons methods.

Ext.application({
	autoCreateViewport: false,
	name: 'MyApp',

	requires: [
	],

	appFolder: 'myapp/app',

	launch : function () {
		// Load the app navigation buttons
		Ext.Ajax.request({
			url: './url/to/get/refdata',
			scope:this,
			success:this.onLoadNavButtons,
			failure:function () {
				Ext.msg.alert("My App Initialization Failed", "Initial Loading Error");
			}
		});
	},

	onLoadNavButtons : function(xhr) {
		var viewPortData = Ext.decode(xhr.responseText),
			i, tot;
		this.navButtons = viewPortData.navButtons;
		this.defaultAction = viewPortData.defaultAction;
		this.viewportMetaData = viewPortData.viewportMetaData;
		this.buildViewPort();
	},

	buildViewPort : function () {
		this.viewport = Ext.create( "MyApp.view.Viewport", {
			viewportMetaData : this.viewportMetaData,
			navButtons: this.navButtons
		});
		var c = this.getController('Viewport'),
			da = this.defaultAction;
		c.init();
		this.runAction(da.controllerName, da.actionName, da.extra);
	},

	runAction : function (controllerName, actionName) {
		Ext.log(controllerName + ' ' + actionName);
		var controller = this.getController(controllerName),
			viewPort = this.getController('Viewport');
		controller.init(this);
		controller['action'+actionName].apply(
		    controller, Array.prototype.slice.call(arguments, 2));
		viewPort.toggleButtons.apply(viewPort,Array.prototype.slice.call(arguments, 2));
	},

	setMainView: function(view) {
		var center =  this.viewport.layout.centerRegion;
		if(center.items.indexOf(view)===-1){
			center.add(view);
        	}
		center.getLayout().setActiveItem(view);
	}
});

And the JSON that is returned from the server is

{
	"viewportMetaData" : {
		"title" : "My App Title Here",
		"hideToolbar" : false
	},
	"defaultAction" : {
		"controllerName" : "Home",
		"actionName" : "Show",
		"extra": "homeBtn"
	},
	"navButtons":[
		{
			"text": "Home",
			"itemId": "homeBtn",
			"iconCls": "icon-house"
		},
		"-",
		{
			"text":"Another Button",
			"itemId":"anotherBtn",
			"iconCls":"icon-door-in"
		},
		"-",
		{
			"text":"One More Button",
			"itemId":"moreButton",
			"iconCls":"icon-arrow-redo"
		}
	]
}

And then, in my Viewport “view” I use the navigation button configs when building the toolbar for the viewport.

Helpful Links:

Version Info: Ext JS 4.1.1

Ext JS 4: A Remote to Local QueryMode Hybrid ComboBox

I’ve needed this multiple times, an Ext JS combobox that is “remote”, but after loading all the combobox data on the first trigger click or when the user types in the combo field, it becomes “local”. Basically, I just don’t want the database hit unless the user actually uses the combobox.

Since this was my second or third time needing this, it’s time for a plugin. (gist 3659866)

And to use it, create the store for your combo box as having autoLoad set to false, and just put the plugin on the combobox.

// Define the model for a State
Ext.regModel('State', {
    fields: [
        {type: 'string', name: 'abbr'},
        {type: 'string', name: 'name'},
        {type: 'string', name: 'slogan'}
    ]
});

// The data store holding the states
var store = Ext.create('Ext.data.Store', {
    model: 'State',
	autoLoad: false,
	proxy: {
		type: 'ajax',
		url: '/states.json',
		reader: {
			type: 'json',
			root: 'states'
		}
	}
});

// Simple ComboBox using the data store
var simpleCombo = Ext.create('Ext.form.field.ComboBox', {
    fieldLabel: 'Select a single state',
    renderTo: 'simpleCombo',
    displayField: 'name',
    width: 500,
    labelWidth: 130,
    store: store,
    queryMode: 'remote',
    typeAhead: true,
    plugins: ['remotetolocalcombo']
});

Version Info: Ext JS 4.1.1

Ext JS 4: Load a Data Store using JSON params

Update 12/5/2012: Recently came across Skirtle’s excellent write up about custom proxies and I realized that my JSON Ajax proxy was overly complicated. Now it is simplified and results in less overriding of the code.


In Ext JS 4.1, when you have a store configured to use an Ext.data.proxy.Ajax proxy and you call store.load(), the request is a GET request with your params url encoded. I’ve managed to work w/this so far, but I now have a case where my params are getting much more involved (nested objects) and JSON is a better fit. Currently there is no way to indicate to the proxy “do this as a POST request, w/JSON”, like you can when calling Ext.Ajax.request directly (just use the jsonData parameter).

After much searching and Ext JS 4.1 source debugging, I extended the Ext.data.proxy.Ajax proxy and created one that will post the params as JSON.

The code is in gist 3370841.

I’m currently starting to really use this, so I’ll update if I find any issues, but basically it just puts your params into the jsonData property, so that when Ext.Ajax.request gets called, it does the right thing. It also says that “reads” are “POST”s, not “GET”s. The encode sorters/filters stuff is overridden to make sure that sorters and filters do not get JSON encoded twice. I’m not using full CRUD on my store, but I think it should all work.

To use it, just use this “jsonajax” as your proxy. Here’s a store config. It uses the new proxy and also has remote sorting and paging.

{
	xtype: 'store',
	storeId: 'myStore',
	model: 'My.Model',
	pageSize: 25,
	remoteSort: true,
	proxy: {
		type: 'jsonajax',
		url : 'my/url/here,
		reader: {
			type: 'json',
			root: 'rows',
			totalProperty: 'total',
			messageProperty: 'message'
		}
	}
}

Helpful links:

Version Info: Ext JS 4.1.1

Ext JS 4: Twitter Bootstrap Style Dismissable Alerts

I don’t know why, but I keep changing the style of notification/alert messages that I am using in my applications. Probably not a good thing to have different styles in my application, but I guess I haven’t settled on the perfect one yet.

I’m using another one I made (Notification Bar) a lot in my applications when I have a grid or panel with a toolbar. Now I wanted one that is a little more modern looking. As a result, I created a simple Ext JS 4 component with dismissable alert messages, styled like the alerts in Twitter’s Bootstrap.

First a screenshot of them all:
Dismissable Alert screenshot There are a few differences from the Bootstrap look, but I’m not a CSS guru, so I’m not sure why. In addition to Chrome and Firefox, I’ve looked at the alerts in IE6, IE7 and IE8. The ‘x’ is shifted lower in IE6 and IE7, but it’s acceptable.

The new component has some helper methods to show the different types of alerts. The alerts are dismissable. The helper methods will change the css if needed and show the alert if the alert is rendered. The “show” methods do not add the alert to your items. “Dismissing” the alert just hides it. It is not being removed from the DOM.

The code is in gist 3277584.

Here is an example of using the DismissableAlert. I’m including it in my view in the items config, initially as hidden. Without a width, the alert will stretch across to the max of whatever is containing it.

{
	itemId: 'myalert',
	xtype: 'dismissalert',
	width: 500,
	hidden: true
}

Below is an abbreviated version of my controller. A bunch of stuff has been cut out, so it might not completely make sense, but what matters is an example of hiding (in actionShow) and showing the alert (in myAjaxCallback).

Ext.define('My.controller.Home',{
	extend: 'Ext.app.Controller',

	views : [
		'My.view.HomeCard'
	],

	refs : [
		{ ref: 'alertBox', selector: '#myalert'}
	],

	init: function(application) {
		// control stuff here
	},

	actionShow : function() {
		var alertBox = this.getAlertBox();
		if (this.needsRefresh) {
			if (alertBox) {
				alertBox.hide();
			}
			Ext.Ajax.request({
				url: 'ajax/url/here',
				scope: this,
				callback: this.myAjaxCallback
			});
		}
	},

	myAjaxCallback : function (options, success, response) {
		var json, showError = false, alertBox;
		
		// snip
		// other codes that evals AJAX json reponse and does something w/it

		if (showError) {
			alertBox = this.getAlertBox();
			alertBox.showError("An unexpected error occurred loading your latest data.");
		}
	}
});

Version Info: Ext JS 4.1
Tested on Chrome 21, Firefox 14.0.1, IE6, IE7, IE8

Ext JS 4: Another LinkButton

Previously, I needed a link in my application that was used in many places and always handled the same way. I did that by using a button styled as a link and then handling the events by delegating clicks on objects with a specific css class to one handler (“Link” from One Tab to Another).

In a new application I am working on, I want the same kind of link, but with different handlers for each link. Plus, I only need four of them. Enter the “link button”. I searched around but didn’t find anything exactly like I wanted. So, I created my own.

Here is a screenshot. The links are really buttons, styled in a similar manner as my previous post.

Screenshot using a LinkButton

The code is on github at gist: 3277219

Here is the CSS and the JavaScript. One thing to note about the css. The font-size style is in there for IE7 browsers, since the font:inherit does not work in IE 7. Mine is set to x-large b/c that is what I needed.

Here is a config, using the LinkButton. It is just a snippet, but it shows the last row from the screenshot:

{
	xtype: 'fieldcontainer',
	layout: {
		type: 'hbox',
		align: 'middle'
	},
	defaults: {
		style: {fontSize: 'large'}
	},
	items: [
		{
			itemId: 'linkNo4',
			xtype: 'linkbutton',
			width: 70,
			style: {
				fontSize: 'x-large',
				textAlign: 'right'
			}
		},
		{
			xtype: 'label',
			text: 'This screenshot is from Chrome',
			flex: 1,
			margin: '0 0 0 15'
		}
	]
}

Some helpful/related links:

Version Info: Ext JS 4.1
Tested (at least a little bit) on Chrome 21, Firefox 14.0.1, IE6, IE7, IE8.

Ext JS 4: Override timeouts

One way to override all the ajax related timeouts, including store loads and form submits.

Ext.Ajax.timeout= 60000; // 60 seconds
Ext.override(Ext.form.Basic, { timeout: Ext.Ajax.timeout / 1000 });
Ext.override(Ext.data.proxy.Server, { timeout: Ext.Ajax.timeout });
Ext.override(Ext.data.Connection, { timeout: Ext.Ajax.timeout });

This is from a Sencha forum post How to set timeout which is by ext4all

This was helpful for me b/c I wanted to increase the timeout globally across my application. Even though the documentation makes it look like setting Ext.Ajax.timeout will increase all timeouts, it’s just not true. Debugging through the code, I would still see 30 sec timeouts for store loads (b/c it pulls from the Ext.data.proxy.Server timeout). But, this covers it all.

Version Info: Ext JS 4.1

Ext JS 4: Simple Grid with Cell Editing

I have a straightforward grid with two columns that has cell editing enabled. Two requirements to implement:

  1. Only new rows can edit the first column, named “Key”.
  2. Make sure the cell display is html encoded

Here is how I configured the CellEditing plugin:

var cellEditing = Ext.create('Ext.grid.plugin.CellEditing', {
	pluginId: 'cellplugin',
	clicksToEdit: 1,
	listeners : {
		scope: this,
		beforeedit: function(e, options) {
			// Allow edits in the first column to new rows only
			if (options.colIdx === 0) {
				if (options.record.phantom === true) {
					return true;
				}
				return false;
			}
		}
	}
});

And here is how I configured the grid. Interesting parts are selType, plugins and the renderer on the columns.

var myGridConfig = {
	xtype: 'grid',
	store: this.myStoreStore,
	selType: 'cellmodel',
	plugins: [
		cellEditing
	],
	columns : [
		{text: 'Key', dataIndex: 'key', editor: 'textfield', renderer: Ext.util.Format.htmlEncode},
		{text: 'Value', dataIndex: 'value', flex: 1, editor: 'textfield', renderer: Ext.util.Format.htmlEncode}
	]
}

Version Info: Ext JS 4.1

Related posts: https://ahlearns.wordpress.com/2012/03/02/ext-js-4-cell-editing-and-blur-event/