EXTJS in XPages #3 – Creating a basic grid from a Custom Control

In this article I will demonstrate how to create an EXTJS grid representation of a view using a custom control and 3 custom properties. At the end of this article you will be able to drag and drop a custom control onto your XPage, give it the count, the viewName and the name of the placement element and a grid will be created for you without any further coding.

Introduction

In XPages we want to be able to genericize everything down to a re-usable custom control and in this example I will show you how to add a grid to your XPage doing just that. The following XPage source code is all that is needed to create this basic grid capability.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xc="http://www.ibm.com/xsp/custom">

	<xc:ccBasicExtJSGrid countNum="10" putGridHere="gridHere" theViewName="ByName-First5Col" ></xc:ccBasicExtJSGrid>

	<div id="gridHere" class="extjsGrid"></div>

</xp:view>

Demonstration

The EXTJS in XPages demonstration database can be found at http://demo.xomino.com/xomino/extjs.nsf

Download

The sample database that will accompany this series is  available from the menu in the live demo database from this link – http://demo.xomino.com/xomino/Extjs.nsf

The Custom Control

The Custom Control is created with 3 Properties which are populated when it is added to the XPage. You add it to your XPage by dragging and dropping and adding the 3 properties:

xt31

The View

The view ByName-First5Col is a simple view of 5 columns containing a single field in each column but as we will see this control will handle any number of columns.

ext32

The code

The end result is to create the JavaScript necessary to create a Basic grid in the same fashion as I did in the first article. But in this case we are not providing the column names or headers, we are programmatically getting them from the view itself.

The fields are created by going to the view and getting the columns and then of those, the item name (nvc.getItemName())

var fields=[
		#{javascript:
				var view:NotesView = database.getView(compositeData.theViewName)	//get the view
				var msg=[];
				for (var i=1; i<view.getColumnCount()+1; i++){
					var nvc:NotesViewColumn = view.getColumn(i);			//get each view column in turn
					msg.push('\''+nvc.getItemName()+'\'')				//get the column programmatic name
				}
				return msg.join(',')							//return the string of fields
		}
	]

The columns are created in the same maner but instead of getting just the column Item Name we get the column Title (nvc.getTitle() as well)

var columns=[
	#{javascript:
			var view:NotesView = database.getView(compositeData.theViewName)
			var msg=[];
			for (var i=1; i<view.getColumnCount()+1; i++){
				var nvc:NotesViewColumn = view.getColumn(i);
				//Create the JSON object containing the column programmatic name and the column title form the view
				msg.push('{header: \''+nvc.getTitle()+'\', sortable: true, dataIndex: \''+nvc.getItemName()+'\'}')
			}
			return msg.join(',')
		}
	]

I added the REST control to the Custom Control itself for simplicity and to make it more portable. This does have the restriction (at this time) of only having one grid on a page at a time. The pathInfo and id of a REST control cannot apparently be computed at build time so I have to keep them static. I am sure there is a way around it…in the mean time the viewName is passed in via compositeData as well as the count. As I think about it you could probably give control to the user using scoped variables and have them select the number of documents displayed…but that is for later.

	<xe:restService pathInfo="ccREST" id="restServe1" >
		<xe:this.service>
			<xe:viewJsonService defaultColumns="true" count="${javascript:compositeData.countNum}" viewName="${javascript:compositeData.theViewName}"></xe:viewJsonService>
		</xe:this.service>
	</xe:restService>

The viewName is also passed into the EXTJS code so that it knows where to look for the REST service on the page.

    var store = Ext.create('Ext.data.Store', {
        autoLoad: true,
        autoDestroy: true,
        autoSync: true,
        model: 'Person',
        proxy: {
            type: 'rest',
            url: '#{javascript:facesContext.getExternalContext().getRequest().getRequestURI()+"/ccREST"}',
            reader: {
                type: 'json',
                root: 'items'
            },
            writer: {
                type: 'json'
            }
        }
    });

The EXTJS code is then simplified because we are passing in the fields and columns values already computed above. Below is the code for the whole Custom Control.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xe="http://www.ibm.com/xsp/coreex">
	<script type="text/javascript" src="ext-4.1.1/ext-all.js"></script>
	<link rel="stylesheet" type="text/css" href="ext-4.1.1/resources/css/ext-all.css" />

	<style>
		.icon-user { background-image: url(images/user.png) !important; }
		.icon-add { background-image: url(images/add.png) !important; }
		.icon-delete { background-image: url(images/delete.png) !important; }
	</style>

	<xp:scriptBlock id="scriptBlock1">
		<xp:this.value><![CDATA[

		var fields=[
			#{javascript:
					var view:NotesView = database.getView(compositeData.theViewName)
					var msg=[];
					for (var i=1; i<view.getColumnCount()+1; i++){
						var nvc:NotesViewColumn = view.getColumn(i);
						msg.push('\''+nvc.getItemName()+'\'')
					}
					return msg.join(',')
			}
		]

		columns=[
			#{javascript:
					var view:NotesView = database.getView(compositeData.theViewName)
					var msg=[];
					for (var i=1; i<view.getColumnCount()+1; i++){
						var nvc:NotesViewColumn = view.getColumn(i);
						msg.push('{header: \''+nvc.getTitle()+'\', sortable: true, dataIndex: \''+nvc.getItemName()+'\'}')
					}
					return msg.join(',')
				}
			]

		Ext.Loader.setConfig({
		  enabled : true,
		  disableCaching : false
		});

		Ext.require([
		             	'Ext.data.*',
		             	'Ext.grid.*',
		             	'Ext.ux.grid.FiltersFeature'
		         ]);

		Ext.define('Person', {
		    extend: 'Ext.data.Model',
		     fields: fields
		});

		Ext.onReady(function(){

		    var store = Ext.create('Ext.data.Store', {
		        autoLoad: true,
		        autoDestroy: true,
		        autoSync: true,
		        model: 'Person',
		        proxy: {
		            type: 'rest',
		            url: '#{javascript:facesContext.getExternalContext().getRequest().getRequestURI()+"/ccREST"}',
		            reader: {
		                type: 'json',
		                root: 'items'
		            },
		            writer: {
		                type: 'json'
		            }
		        }
		    });

		    var local = true;

		     var filters = {
		        ftype: 'filters',
		        // encode and local configuration options defined previously for easier reuse
		        //encode: encode, // json encode the filter query
		        local: local   // defaults to false (remote filtering)

			};

		    var grid = Ext.create('Ext.grid.Panel', {
		        renderTo: 'gridHere',
		        frame: true,
		        features: [filters],
		        height: 400,
		        title: 'Users',
		        store: store,
		        iconCls: 'icon-user',
		        columns: columns
		    });
		});
			]]></xp:this.value>
	</xp:scriptBlock>

	<xe:restService pathInfo="ccREST" id="restServe1" >
		<xe:this.service>
			<xe:viewJsonService defaultColumns="true" count="${javascript:compositeData.countNum}" viewName="${javascript:compositeData.theViewName}"></xe:viewJsonService>
		</xe:this.service>
	</xe:restService>
</xp:view>

Results

As you can see below – a grid, same as we had before.

extjs33

But now we can add a new column to view and without changing the control itself the new column appears on the web.


ext34

ext35

And even complex columns with combined field values.

ext37 ext36

Conclusions

With this Custom control we are able to begin to create the ability to add these EXTJS grids to our XPages, very quickly and very effectively. As we go through the series I will update the custom control with the new capabilities as we look at new grids. Eventually we will have a plethora of controls which can be used to create powerful grids in next to no time at all.

In the mean time you can download the control from the sample database and play with it yourself. http://demo.xomino.com/xomino/Extjs.nsf

10 thoughts on “EXTJS in XPages #3 – Creating a basic grid from a Custom Control

    • Thanks Shean, I always appreciate feedback – glad you are enjoying it.

      I am sticking to pure reporting for right now but it is certainly “do-able” in a number of different ways – we will get there 🙂

  1. I would be very curious to know how you did it “Grid With Pager”.
    Can you post an example?
    The database that you shared is out of date.
    thank you very much

  2. Stefano – yeah the examples download database is in line with the blog series – the extra examples are stuff I have been working on for upcoming articles – will update the download database soon 🙂

    thanks for reading 🙂

  3. I played around with your examples. Here are my suggestions for optimization:
    To use multiple custom controls of this grid I would extend the JS in the script block to dynamic variables for “fields” and “columns”, such as

    var fields#{javascript:this.getParent().getId()}= …

    so that the variable names are calculated with the id of the CC in the xpage.

    Further I added a grouping feature to the grid:

    ‘Ext.grid.feature.Grouping’

    (in the store optional a groupField: ‘state’ for example

    in the grid: features: [filters, {ftype:’grouping’, groupHeaderTpl:'{name} ({children.length})’}]

    Looking forward to see more 🙂

    • Thank you Oliver – nice suggestions!!

      Yes Grouping – definitely going to get to that in probably article #9 or #10 with the new 4.2 BufferedRenderer infinite scroller which is just amazing!!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s