grid

Ext JS 4: Ext.ux.RowExpander Tweaks

I recently looked into a few issues we were experiencing with the Ext.ux.RowExpander plugin, which resulted in a few small tweaks to the plugin.

The setup: Grid with checkbox selection model using Ext.ux.RowExpander plugin.  An expanded row is a nested grid.  This was implemented following a pattern that can be found around the web about nesting grids in the expanded rowbody (http://stackoverflow.com/questions/8254113/nested-grid-with-extjs-4).

Issue 1: When the row expands to show the nested grid, the expanded row takes the full width of the grid, instead of aligning to the right of the checkbox column and the expand/collapse column.  This is actually logged as a bug, with pictures at http://www.sencha.com/forum/showthread.php?263231-Content-inside-RowExpander-doesn-t-take-all-width-of-a-row-if-grid-has-CheckboxModel.

The problem is that within the RowExpander plugin, the logic deciding the colspan of the expanded row body does not take into account any other grid features that might impact the columns.  I updated the plugin code to look at the parent grid selection model before determining how wide the colspan should be.

Issue 2: In my UI there is a search panel and then the grid with the search results.  The user can go through and expand various rows, but if the user runs a search again I want the search results to return and the grid refreshed with the results and all the rows collapsed.  What was happening was the grid was refreshed, all rows collapsed, but the expand/collapse icon was in the expanded state.

To fix this I added a method, clearExpanded to the RowExpander plugin.  It’s very straightforward and just clears out the rowsExpanded property the plugin maintains to track which rows are expanded.  Then, I listen for the refresh event on the parent grid’s view, and at that point, call clearExpanded on the plugin.  The refresh event handler on the grid view looks like this:

onMyGridRefresh: function() {
    var rowExpander = this.getSearchGrid().getPlugin('rowexpanderplugin');
    if (rowExpander) {
        rowExpander.clearExpanded();
    }
    this.destroyNestedGrids();
}

Remember, if you are creating nested grids in the manner described above, you need to destroy those grids manually!  Hence, the destroyNestedGrids() method in the code above.  It does a component query on the nested grid xtype and calls destroy on the returned components.

Solution: That’s great, where’s the code?

The updated plugin is at Gist https://gist.github.com/aghuddleston/25b19dfe10f7eb40e54c.   It’s a little long to directly include here.

Version Info: Ext JS 4.1.1

Advertisements

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: 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/

Ext JS 4 – Plugin to Add Icons to a Panel Header

Recently in my app I had multiple grid panels that needed icons added to the header, where the title goes. Instead of duplicating the code in multiple places, I created a simple plugin that I could use on any panel anywhere in my application.

Here is the code of the plugin. It’s really simple, but helps keep my grid/panel code cleaner. Gist can be found here Panel Header Extra Icons, version 1.

And example usage of a grid config with the plugin:

{
	xtype: 'grid',
	title: 'My Grid Title',
	columns : [
	],
	plugins: [{
		ptype: "headericons",
		headerButtons : [
			{
				xtype: 'button',
				iconCls: 'icon-page-white-excel',
				scope: this,
				handler: this.onExportToExcel
			}
		]
	}]
}

And here is a screenshot, that shows two different panel headers that I have. One with one icon and one with two.
Screenshot of Icons added to Panel Header

Updated 03/22/2013
Per comments, I updated the plugin a little bit. I also had my own changes that I hadn’t updated here… Here is the updated plugin. Feedback??? Gist can be found here https://gist.github.com/2770422.

Version Info: Ext JS 4.1.1

Ext JS 4: “Link” from One Tab to Another

I have a pretty typical Ext JS app.  Tab panels and within each tab border layouts with multiple grids.  One thing the app does is manage users and one tab shows user details.  Anywhere else in the app that shows a user name, I want to link that user name to the user detail (not in a dialog, but actually switch to the user detail tab).  Typically, the user names are listed in grids.  Here is what I did to hook up the “links” to the tab.

I started out using an <a> tag, but settled on a <button> tag to avoid the hash being added to the url and messing up my history.

Here is the renderer function for when the links are in a grid.  It’s pretty straightforward, just renders the cell with button tag.


userNameLink : function(val) {
    return Ext.String.format('<button type="button" class="userNameLink">{0}</button>', val);
}

Now style the link.  My css skills leave a lot to be desired.  I found this very helpful post which I used for the styling: Styling button elements to look like links


button.userNameLink {
     overflow:visible; 
     margin:0;
     padding:0;
     border:0;
     color: #2B547D;
     background:transparent;
     font:inherit;
     line-height:normal; 
     text-decoration:underline; 
     cursor:pointer; 
     -moz-user-select:text; 
}

button.userNameLink::-moz-focus-inner {
     padding:0;
     border:0;
}

Finally, now to do something with those “links”.  Right now they look good, but they aren’t doing anything!


Ext.getBody().on('click', this.handleUserNameLink,
    this, {
        delegate: 'button.userNameLink'
    });

The “handleUserNameLink” function does the heavy lifting of actually switching tabs and defaulting data, but this nifty code adds the handler to every single one of my links w/o me having to add it myself everywhere.

Helpful links:

Version info: Ext JS 4.0.7
Tested on IE6, IE8, Firefox 11, Chrome 18

Ext JS 4 – Cell Editing and Blur Event

I have a grid panel using the cell editing plugin. There is also a Save button on panel top toolbar. When editing a field in the grid and then clicking the save button without tabbing out of the field being edited the blur event of the cell occurred after the Save button handler. It happened consistently in IE8.

I found this very helpful post EditorGridPanel -delay in firing Blur Event causes Bug on the Sencha forums and utilized the same solution, with a slight update for Ext 4. I called plugin.completeEdit() at the beginning of my save handler.

Plugin declaration:

var cellEditing = Ext.create('Ext.grid.plugin.CellEditing', {
	pluginId: 'cellplugin',
	clicksToEdit: 1,
	listeners : {
		scope: this,
		// put listeners here
	}
});

At the beginning of the save button handler:

onSave : function () {
	var grid = this.child('#divGrid'), plugin;

	plugin = grid.getPlugin('cellplugin');
	plugin.completeEdit();
	
	// now do what is needed for save
}

Ext JS Version: 4.0.7

Ext JS 4: Grid emptyText

As of Ext JS 4, the emptyText that can display in grid has no styling. It just sits smack up against the top and left sides of the grid. One simple way to style the text is to add some css and put the emptyText inside a span.

.emptyText {
    position: relative;
    top: 10px;
    left: 10px;
}

Of course, more styling such as color and font-size could be added.

And then, when defining your grid, just do this:

{
	xtype: 'grid',
	viewConfig: {
		emptyText: '<span class="emptyText">No rows found.</span>'
	},
	//...
}

I haven’t started using Ext JS 4.1 yet, so I’m not sure if the lack of style for the emptyText will still apply. But until then, this makes the empty text look a little better.

Ext JS 4 – Wrap Text in One Grid Column

I have a grid that is read only and where I’ve disabled the column menus except for sorting. One column is a “notes” column so the text can easily overflow the cell. By default Ext JS elliptifies the text, but in this case, I just want all the text to display in the cell. I ended up just adding a little bit of css to get it to work.

td.wrap-text div {
    white-space: normal;
}

And in my column definition, used the css class on the td for the grid cell. Below I have a grid panel that occupies the center panel of a border layout.

{
	region: 'center',
	xtype: 'grid',
	store: this.myStore,
	viewConfig: {
		emptyText: 'No data found'
	},
	features: [{ ftype: 'selectable' }],
	columns: [
		{header: 'Date', sortable: true, dataIndex: 'createdDate', xtype: 'datecolumn', format: 'm-d-Y  H:i', flex:1, menuDisabled:true},
		{header: 'Event', sortable: true, dataIndex: 'myEvent', flex:2, menuDisabled:true},
		{header: 'Created By', sortable: true, dataIndex: 'createdBy', flex:2, menuDisabled:true},
		{header: 'Notes', sortable: true, dataIndex: 'description', flex:5, menuDisabled:true, tdCls:'wrap-text'}
	]
};

And, this worked. Even on IE6.

Helpful links:
http://www.learnsomethings.com/2011/08/25/make-all-the-text-in-the-cells-in-only-one-grid-wrap/
http://www.sencha.com/forum/showthread.php?125219-long-text-in-grid-cell

Ext JS 4 – Adding qtip to a Gridpanel Cell

For one column in a grid I needed to display an image and I wanted a tooltip to display when the mouse is over the image. My custom renderer looks like this:

		var iconRenderer = function(value, meta, record, rowindx, colindx, store) {
			meta.tdCls = 'blocked';
			meta.tdAttr = 'data-qtip="Blocked"';
			return value;
		};

The css for ‘blocked’ looks like this:

td.blocked {
	background: url(/images/stop.png) no-repeat center !important;
}

Thanks to http://existdissolve.com/2011/07/adding-qtips-to-gridpanel-rows-in-extjs-4-0/ for pointing out two important changes in Ext 4:

  1. The names of the fields on the meta object changed. tdAttr instead of attr
  2. The name for the qtip changed from qtip to data-qtip. The documentation about putting “data” in front of qtip is in the Ext JS documentation for the QuickTipManager.

Ext JS – Make Grid Text Selectable (with a mouse)

For some unfathomable reason, Ext JS makes text in a grid unselectable. You cannot select the text in the grid with your mouse to copy and paste it. The second you show a user a grid, the first thing he or she wants to do with it is select the text! The workarounds for this are well documented in the Grid FAQ and also in some of the threads on the Sencha forum. This is my “roundup” of the solutions.

For Ext JS 3
(solution is directly from the Grid FAQ) and it looks like this:

CSS to add:

<style type="text/css">
	.x-selectable, .x-selectable * {
		-moz-user-select: text!important;
		-khtml-user-select: text!important;
	}
</style>

To make the text selectable on one grid:

var grid = new Ext.grid.GridPanel({
   viewConfig: {
      templates: {
         cell: new Ext.Template(
            '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id}
                        x-selectable {css}" style="{style}" 
                        tabIndex="0" {cellAttr}>',
            '<div class="x-grid3-cell-inner x-grid3-col-{id}"
                        {attr}>{value}</div>',
            '</td>'
         )
      }
   },
   ...
});

To make all the grids have selectable text:

if (!Ext.grid.GridView.prototype.templates) {
   Ext.grid.GridView.prototype.templates = {};
}
Ext.grid.GridView.prototype.templates.cell = new Ext.Template(
   '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} x-selectable {css}"
               style="{style}" tabIndex="0" {cellAttr}>',
   '<div class="x-grid3-cell-inner x-grid3-col-{id}" {attr}>{value}</div>',
   '</td>'
);

For Ext JS 4 (4.0.7 in my case)
Solution is from this thread in the forums:
Make grid unselectability a boolean attribute

Add this css – I had to modify what is in the thread a little bit and add the changes to .x-grid-cell-inner for the selection to work on Chrome

.x-grid-cell-inner, .x-selectable {
    -moz-user-select: text !important;
    -khtml-user-select: text !important;
    -webkit-user-select: text !important;
}

Define a new “feature” that can be added to a grid

Ext.define('xxx.grid.SelectFeature', {
    extend: 'Ext.grid.feature.Feature',
    alias: 'feature.selectable',

    mutateMetaRowTpl: function(metaRowTpl) {
        var i,
        ln = metaRowTpl.length;

        for (i = 0; i < ln; i++) {
            tpl = metaRowTpl[i];
            tpl = tpl.replace(/x-grid-row/, 'x-grid-row x-selectable');
            tpl = tpl.replace(/x-grid-cell-inner x-unselectable/g, 'x-grid-cell-inner');
            tpl = tpl.replace(/unselectable="on"/g, '');
            metaRowTpl[i] = tpl;
        }
    }
});

Add the following override to your Ext application:

 Ext.override(Ext.view.Table, {
    afterRender: function() {
        var me = this;

        me.callParent();
        me.mon(me.el, {
            scroll: me.fireBodyScroll,
            scope: me
        });
        if (!me.featuresMC &&
            (me.featuresMC.findIndex('ftype', 'unselectable') >= 0)) {
            me.el.unselectable();
        }

        me.attachEventsForFeatures();
    }
});

And last step, add the feature to your grid:

{
    xtype: 'grid',
    features: [{ ftype: 'selectable' }],
    // All the other configs for a grid
}

Hopefully Sencha will just make this a standard feature of grids, or preferably a config on the grid. In the meantime, it’s a lot of steps, but necessary.

UPDATE: For Ext JS 4.1+
Config is now available on the grid view. The new config is enableTextSelection. http://docs.sencha.com/ext-js/4-1/#!/api/Ext.grid.View-cfg-enableTextSelection. Set enableTextSelection to true in the viewConfig of your grid.