Monthly Archives: May 2014

Node.JS 101: Querying a Database and Outputting a Dynamic HTML Table with Express and Jade

Last night I gave a presentation about “What’s New in ColdFusion 11” to our ColdFusion meetup in DC. What’s new, of course, is that Adobe has put a lot of effort into making ColdFusion a lot more like JavaScript by significantly enhancing CFScript- an alternative to the ColdFusion Markup Language (CFML) that has syntax similar to JavaScript.

Of course, CFScript isn’t JavaScript. It’s not even similar to Microsoft’s jScript (shudder). And switching between CFScript programming and JavaScript programming is a little like switching between playing ping pong and playing tennis. They’re kinda similar, but playing a lot of ping pong is likely to screw up your tennis game, and vice-versa.

The fact of the matter is that if a CF developer really wants to program in Javascript, then they should just learn to stop worrying and take a close look at Node.JS. Node.js is a platform built on Chrome’s JavaScript runtime. It’s ridiculously fast and pretty straightforward to learn – assuming that you’re already familiar with basic JavaScript concepts.

For example, let’s take a look at a bit of Node code that queries a MySQL database table and subsequently transmits the results to a JADE template:


// create the connection 
var connection = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: ''
});

var sql = 'SELECT firstName,lastName from NodeJSExamples.Person';
connection.connect();

connection.query(sql, function(err, rows, fields) {
  if (err) throw err;
  res.render('users', { title: 'Users', rows: rows });
});

connection.end();

Pretty straightforward, right?

Now let’s take a look at the code that dumps out the rows into an HTML table via the Jade templating engine:

extends layout

block content
  h1= title
  p #{title} Listing

  table
    thead
      tr
        th First Name
        th Last Name
    tbody
      - each item in rows
        tr
          td= item.firstName
          td= item.lastName

Is this syntax superior (and even less verbose) than CFScript? You be the judge. And if you find yourself intrigued by the possibilities of Node.js, check out our new, no-nonsense, one-day instructor led course – Node.JS Fundamentals!

Simple barebones CF10/CF11 WebSocket Example

Here’s a bare-bones example of setting up an HTML5 WebSockets chat service using ColdFusion 10/11:

<!--- application.cfc --->
<cfcomponent>
  <cfset this.name = "Cf11Examples">
  <cfset this.wschannels = [{name="chat"}]>
</cfcomponent>
<!--- index.cfm --->
<cfwebsocket name="webSocketObj"
             onMessage="messageHandler"
             onError="errorHandler"
             onOpen="openHandler"
             onClose="closeHandler"
             subscribeTo="chat"/>
             
<doctype html>
<head>
	<title>WebSocket Example</title>
	<script type="text/javascript">
		
		messageHandler =  function(aEvent,aToken) {
		 
		  if (aEvent.data) {
           var txt=document.getElementById("msgArea"); 
           txt.innerHTML += aEvent.data  +"<br />"; 
          }
		}
		
		openHandler = function() {
			alert("Connection is open");
		}
		
		closeHandler= function() {
			alert("Connection Closed");
		}
		
		errorHandler = function() {
			alert("Doh!");
			console.log(arguments);
		}
		
		sendMessage = function() {
			var text = window.prompt("Enter some text","");
			if (text) {
				webSocketObj.publish("chat", text);
			}
		}
	</script>
</head>
<body>
 	<div id="msgArea" />
 	<input type="button" value="Send Message" onClick="sendMessage()">
</body>
</html>

Upgrading to Ext JS 5 in five easy steps!

Historically, the upgrade path between different versions of Sencha Ext JS has been challenging for developers and product managers alike. Much to the chagrin of project managers everywhere, migrating apps from Ext JS 3 to Ext JS 4 typically required major refactoring, frequently necessitating a complete rewrite. The complaints of the IT community were clearly heard loud and clear by Sencha which, I’m happy to report, has actually made the Ext 4 to Ext 5 upgrade path nearly seamless.

Top 5 reasons for upgrading to Ext JS 5

  1. New Components
    Ext 5 adds support for a drag & drop dashboard component, an improved combo box with tag selector, and grid widgets.

    gridwidgets

    Ext JS 5 Grid Widgets

  2. A new charting package based on SVG and Canvas
    The new charting engine is going to be shared with Sencha Touch. It’s a lot simpler for developers to add additional sprites to the drawing layer and floating axis and more complex user interactions are now supported. Sencha has also cleaned up some of the chart config properties (e.g. axis types are now all lower-case) to make it more consistent across the framework. Since the charts are now SVG/Canvas based, client-side methods have been added that enable a user to download the generated chart as a bitmap. You won’t have to rely on the Sencha.io cloud service anymore to convert vectors into bitmaps.

    chart

    Ext JS 5 chart with added custom sprites

  3. Viewmodels and Bi-Directional Data Binding
    You now have the option of using an Model-View-ViewModel (MVVM) architecture instead of the Ext 4.x MVC framework. MVVM enables you to use views expressly for layout while compartmentalizing event handling into a separate, view-specific file. ViewModels enable you to easily implement bi-directional databinding with your views, which significantly cuts down on the amount of code that you’ll need to write (and debug!).

    Combining MVVM with MVC

    Combining MVVM with MVC

  4. Chained Stores and other Improvements to the Data Package
    Most apps are going to require multiple views of the same data store. For instance, you might want to simultaneously output two grids that refer to the same core dataset but have different client-side filters applied to them. In Ext 4 this typically required the instantiation of two separate stores and the duplication of records in memory. Chained stores are analogous database table views.  They link back to a single  “source” store containing your records. The chained store’s  filters, sorters and groupers are defined independently of the data “source” Any updates to the source store automatically triggers a refresh in its associated chained stores.
  5. Tablet Compatibility
    I saved the best for last. Ext JS 5 now supports touch gestures (pinch, rotate, longpress, etc) and comes bundled with two touch-compatible themes – “Crisp” and “Neptune Touch.”  You’ll likely need to make some slight changes to your layouts after applying these themes, however, in most cases you should be able to support both desktop and tablet browsers from a single codebase.

    tablet

    Using the “Crisp” touch-compatible theme

Upgrading to Ext JS 5 in Five Easy Steps

During this tutorial, you’ll upgrade the Congressional Spending Portal – an app that I had initially developed using Sencha Architect in Ext JS 4.1.

Upgrade this app from Ext 4 to Ext 5 in five easy steps!

Upgrade this app from Ext 4 to Ext 5 in five easy steps!Download the tutorial assets

Before you begin

Step 1: Create a Sencha Command Project

Open a command prompt to /extjs5upgrade/ and enter the following statement:

sencha generate app -ext SpendingPortal spendingportal

Note that Sencha Command will automatically download and install the latest version of Ext 5 from the CDN! Cool!

Step 2: Copy the Ext 4 classes into the Ext 5 project

  1. Delete the controller, model, store, and view folders from /extjs5upgrade/spendingportal/app
  2. Copy /extjs5upgrade/before/app/*.* to /extjs5upgrade/spendingportal/app
  3. Copy /extjs5upgrade/before/app.js to /extjs5upgrade/spendingportal/app/Application.js

Step 3: Tweak the Application.js file

Refactor the /extjs5upgrade/spendingportal/app/Application.js file to resemble the following:

Ext.define('SpendingPortal.Application', {
    extend: 'Ext.app.Application',
    
    name: 'SpendingPortal',
   
    controllers: [
        'Main',
        'Sponsors',
        'Earmarks',
        'Feedback'
    ],

    launch: function () {
        // TODO - Launch the application

        var pnl = Ext.ComponentQuery.query('#centerpanel')[0];

        Ext.widget('sponsors', {
            constrainTo: pnl.getEl(),
            x: 5,
            y: 20
        });

        Ext.widget('earmarksviewer', {
            constrainTo: pnl.getEl(),
            x: 500,
            y: 20
        });

        Ext.widget('sponsorchart', {
            constrainTo: pnl.getEl(),
            x: 500,
            y: 300
        });
    }
});

Tweak the /extjs5upgrade/spendingportal/app.js file to set autoCreateViewport to true.

Ext.application({
    name: 'SpendingPortal',
    extend: 'SpendingPortal.Application',
    autoCreateViewport: true
});

Step 4: Invoke Ext 4 Compatibility mode

Open the app/app.json file and add the following code to enable “compatibility” mode:

"compatibility" : {
        "ext" : "4.2"
}

Modify the requires[] property in the app/app.json file to load the Ext 4 charting package:

"requires": [
  "ext-charts"
],

Step 5: Add Tablet Support

Modify the theme property in the /extjs5upgrade/spendingportal/app/app.json file to resemble the following:

"theme": "ext-theme-crisp"

Test the App!

Return to your command prompt and change directories to /extjs5upgrade/spendingportal. Then issue the following command to update the bootstrap.json file which indicates the classes to be loaded:

sencha app refresh

Then tell Sencha Command to create a build and make it accessible via http (Sencha Command has a built-in webserver):

sencha app watch

Test the app in your browser by accessing the URL generated by Sencha command (typically http://localhost:1841).

Now wasn’t that easy?

Congrats! Now that you’ve gotten the app up and running on Ext 5, you can start to improve upon it by invoking the broad range of new features (including Sencha Charts!) that have been added to the framework. Check back here often to learn about new Ext 5 tips, traps, and techniques. And consider engaging Fig Leaf Software for your Sencha consulting and training needs!

Ext JS 4 Fundamentals: Editing Database Tables with the Grid

Every app has “lookup” tables – those pesky little things that associate labels with numeric, autoincrementing primary keys. And those tables typically need to be editable by system administrators in order to maintain the flexibility of the system of which they are a part.

Using the following template-based approach. to quickly build these interfaces by leveraging the Ext JS 4 grid control as illustrated below:

Image

 

The first step in building this GUI is to define a data model and proxy. The model specifies the names of the fields that will be available as a JSON or XML feed. The proxy specifies the url from where the data feed will be generated. Most lookup table models will only specify two or three fields. Ext JS 4 considers the field named “id” to contain the primary key of the record. At Fig Leaf Software, we prefer to use a RESTful data services implementation. Ext automatically transmits a GET/POST/PUT/DELETE request header based on the type of operations that we’re going to perform, which can be a big time saver. All application servers (including Adobe ColdFusion) support the creation of REST-based services.

Ext.define('MyApp.model.PracticeCategory', {
    extend: 'Ext.data.Model',

    requires: [
        'Ext.data.Field',
        'Ext.data.proxy.Rest'
    ],

    fields: [
        {
            name: 'id',
            type: 'int'
        },
        {
            name: 'label',
            type: 'string'
        },
        {
            name: 'isActive',
            type: 'boolean'
        }
    ],

    proxy: {
        type: 'rest',
        url: 'http://[some domain]/[some app name]/category'
    }
});

The next step is to define a data Store which will hold multiple data model instances/records. This part is usually pretty boilerplate since most lookup tables typically contain fewer than 100 records. If you’re expecting more data points, you might want to look at setting the remoteFilter and remoteSort properties, as well as implementing data pagination.

Ext.define('MyApp.store.PracticeCategories', {
    extend: 'Ext.data.Store',

    requires: [
        'MyApp.model.PracticeCategory'
    ],

    constructor: function(cfg) {
        var me = this;
        cfg = cfg || {};
        me.callParent([Ext.apply({
            autoLoad: true,
            model: 'MyApp.model.PracticeCategory',
        }, cfg)]);
    }
});

Now that the data package plumbing has been completed, we’re ready to generate the editing GUI. In order to compartmentalize everything in a nice, neat package, I’ve opted to merge the event handling with the view. Some might argue that they’d prefer putting the event handling into a view controller, but for this specific limited use-case, I find that punching everything into a single file is slightly easier to maintain. And since it’s my code, it’s my rules.

Ext.define('MyApp.view.admin.PracticeCategoryEditor', {
    extend: 'Ext.window.Window',
    alias: 'widget.practicecategoryeditor',

    requires: [
        'Ext.grid.Panel',
        'Ext.form.field.Text',
        'Ext.grid.column.CheckColumn',
        'Ext.form.field.Checkbox',
        'Ext.grid.View',
        'Ext.grid.plugin.RowEditing',
        'Ext.panel.Tool'
    ],

    autoShow: true,
    height: 250,
    width: 400,
    layout: 'fit',
    title: 'Practice Categories',

    initComponent: function() {
        var me = this;

        Ext.applyIf(me, {
            items: [
                {
                    xtype: 'gridpanel',
                    header: false,
                    store: 'PracticeCategories',
                    columns: [
                        {
                            xtype: 'gridcolumn',
                            dataIndex: 'label',
                            text: 'Practice Category',
                            flex: 1,
                            editor: {
                                xtype: 'textfield',
                                allowBlank: false
                            }
                        },
                        {
                            xtype: 'checkcolumn',
                            width: 80,
                            dataIndex: 'isActive',
                            text: 'Active',
                            editor: {
                                xtype: 'checkboxfield',
                                inputValue: 'true',
                                uncheckedValue: 'false',
                                listeners: {
                                    change: {
                                        fn: me.onCheckboxfieldChange,
                                        scope: me
                                    }
                                }
                            },
                            listeners: {
                                beforecheckchange: {
                                    fn: me.onCheckcolumnBeforeCheckChange,
                                    scope: me
                                }
                            }
                        }
                    ],
                    plugins: [
                        Ext.create('Ext.grid.plugin.RowEditing', {
                            pluginId: 'roweditor',
                            autoCancel: false,
                            clicksToMoveEditor: 1,
                            listeners: {
                                edit: {
                                    fn: me.onRowEditingEdit,
                                    scope: me
                                },
                                canceledit: {
                                    fn: me.onRowEditingCanceledit,
                                    scope: me
                                }
                            }
                        })
                    ],
                    listeners: {
                        selectionchange: {
                            fn: me.onGridpanelSelectionChange,
                            scope: me
                        }
                    }
                }
            ],
            tools: [
                {
                    xtype: 'tool',
                    handler: function(event, toolEl, owner, tool) {

                        var grid = tool.up('window').down('grid');
                        var rowEditor=grid.getPlugin('roweditor');
                        var rec = Ext.create(grid.getStore().model, {label: 'New Practice Category'});

                        rowEditor.cancelEdit();
                        grid.getStore().insert(0,rec);
                        rowEditor.startEdit(0,0);

                    },
                    itemId: 'btnAdd',
                    tooltip: 'Add New Record',
                    type: 'plus'
                },
                {
                    xtype: 'tool',
                    handler: function(event, toolEl, owner, tool) {
                        var grid = tool.up('window').down('grid');
                        var rowEditor=grid.getPlugin('roweditor');
                        var sm = grid.getSelectionModel();


                        Ext.Msg.confirm(
                        "Delete Practice Category",
                        "Delete " + sm.getSelection()[0].get('label') + "?",
                        function(b) {

                            var store = grid.getStore();
                            rowEditor.cancelEdit();
                            store.remove(sm.getSelection());
                            if (store.getCount() > 0) {
                                sm.select(0);
                            }
                            store.sync();

                        }
                        );
                    },
                    disabled: true,
                    itemId: 'btnDelete',
                    tooltip: 'Delete selected record',
                    type: 'minus'
                },
                {
                    xtype: 'tool',
                    handler: function(event, toolEl, owner, tool) {
                        tool.up('window').down('grid').getStore().load();
                    },
                    tooltip: 'Refresh',
                    type: 'refresh'
                }
            ]
        });

        me.callParent(arguments);
    },

    onCheckboxfieldChange: function(field, newValue, oldValue, eOpts) {

        var rec = field.up('grid').getSelectionModel().getSelection()[0];
        rec.set('isActive',newValue);
    },

    onCheckcolumnBeforeCheckChange: function(checkcolumn, rowIndex, checked, eOpts) {
        return false;
    },

    onRowEditingEdit: function(editor, context, eOpts) {
        var rec = context.record;
        
        // this is a key technique:
        // The insert web service must return a primary key value, 
        // which is subsequently applied to the record in memory.
        // The record.commit() removes the grid's default indicator of modified fields
        rec.save({
            success: function(record,operation) {
                if (operation.action == 'create') {
                  var pk = Ext.decode(operation.response.responseText).id;
                  record.set('id',pk);
                }
                record.commit();
            },
            failure: function(record,operation) {
                Ext.Msg.alert('Operation failed',"Please try again later.");
                console.log(arguments);
            }
        });
    },

    onRowEditingCanceledit: function(editor, context, eOpts) {
        var rec = context.record;
        if (rec.phantom) {
         context.grid.getStore().remove(rec);
        }
    },

    onGridpanelSelectionChange: function(model, selected, eOpts) {

        var delBtn = this.down('#btnDelete');
        delBtn.setDisabled(!selected.length);
    }

});

That’s all folks! You should be able to integrate this solution into your apps with very few modifications other than tweaking the delete prompt text and changing the default text for a new record label.

Happy coding!

Protip: Constraining Ext JS Windows

This is the only methodology that I’ve found which reliably constrains windows to panels.

Step 1: Define your window with a constrain: true property

Ext.define('MyApp.view.MyWindow', {
    extend: 'Ext.window.Window',
    alias: 'widget.mywindow',
    autoShow: true,
    height: 320,
    width: 501,
    constrain: true,
    layout: {
        type: 'fit'
    },
    title: 'My Constrained Window'
});

Step 2: Instantiate the window into a container using the constrainTo property.

  // get reference to parent panel / container
  var parentPanel = Ext.ComponentQuery.query('#dashboardPanel')[0];
  
  // instantiate and constrain the window

  Ext.widget('mywindow', {
     constrainTo: parentPanel.getEl(),
     x: 5,
     y: 20
  });