Decoupling your CSS from your JavaScript, a “well no duh” moment

This weekend I had one of those “oh DUH” moments which I have been struggling with for quite some time. How can I better structure my jQuery selectors and make the code more maintainable? I came across this excellent article – Decoupling your HTML CSS and JavaScript and it all came into focus.

The problem

Using JavaScript selectors (dojo or jQuery) in XPages can be a problem because of the id naming scheme used by the JSP generator “id=”view:_id1:_id2:link5” and how this falls over in a selector. So an alternate to not having to mess with is attributes is to use class selectors $(‘.class’) which is actually much simpler and less troubling to get your head around.

When you are using class selectors for a DOM element which already has a class then awesome I don’t have to add any more classes. Using this bootstrap example there are multiple classes I can get a hold of to manipulate the dialog contents.

 <div class="modal fade myModal" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
			<button type="button" class="close modal-maxmin" aria-hidden="true">
				<i class="icon-external-link"> </i>
			</button>
			<button type="button" data-dismiss="modal" class="close" aria-hidden="true">
				<i class="icon-remove"> </i>
			</button>
          <h4 class="modal-title">modal-title</h4>
        </div>
        <div class="modal-body">
         modal-body : HERE GOES THE MESSAGE
        </div>
        <div class="modal-footer">
	        <div class="btn-group">
	          <button type="button" class="btn btn-default modal-cancel" data-dismiss="modal">CANCEL</button>
	          <button type="button" class="btn btn-primary modal-ok">OK</button>
	        </div>
        </div>
      </div><!-- /.modal-content -->
    </div><!-- /.modal-dialog -->
  </div><!-- /.modal -->

To add something to the body of the dialog I would do something like this

  $(".modal-body").html('Hi Marky')

The other problem

This example is very “coupled” code whereby my JavaScript selector is coupled to the real class file used by the HTML code. In reality what it means is that if I have to change the classname for some reason (hmm an example – oh I don’t know – going from Bootstrap2 to Bootstrap3 for example) then my JavaScript breaks down. While tightly coupled code is easy to follow, easy to create initially, it can be an absolute nightmare in the long run.

The epiphany moment

As I am reading Philip Walton’s article  I am struck by the “oh well no duh” moment when he talks about using js-* as a naming convention for classes which only exist for JavaScript selection.

  • My personal recommendation is to use a prefix for all JavaScript hooks. I use js-*. That way, when a developer sees such a class in the HTML source, she’ll know exactly where to look to discover its purpose.

Well no duh – brilliant idea!

In all senses this makes perfect sense:

  • The developer does not have to search through CSS files looking to see if you used the class as a selector as a real CSS marker
    • (well ok they changed something and the CSS broke, then they started to look for it)
  • It is immediately apparent from looking at the HTML that there is some dojo or jQuery selection/manipulation going to go on in this area because of the js-* class naming scheme
  • Within eclipse you can do a quick search and find out what is trying to manipulate this area before you go and ruin it

The new code

A very simple change to the modal-body but this then decouples my code form the bootstrap code – allowing me to make changes to the bootstrap classes int he future without breaking my JavaScript

 <div class="modal fade myModal" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
			<button type="button" class="close modal-maxmin" aria-hidden="true">
				<i class="icon-external-link"> </i>
			</button>
			<button type="button" data-dismiss="modal" class="close" aria-hidden="true">
				<i class="icon-remove"> </i>
			</button>
          <h4 class="modal-title">modal-title</h4>
        </div>
        <div class="modal-body js-modal-body"> <!-- js-modal-body change here -->
         modal-body : HERE GOES THE MESSAGE
        </div>
        <div class="modal-footer">
	        <div class="btn-group">
	          <button type="button" class="btn btn-default modal-cancel" data-dismiss="modal">CANCEL</button>
	          <button type="button" class="btn btn-primary modal-ok">OK</button>
	        </div>
        </div>
      </div><!-- /.modal-content -->
    </div><!-- /.modal-dialog -->
  </div><!-- /.modal -->

To add something to the body of the dialog I would now select the js-* class not the modal-body itself

  $(".js-modal-body").html('Hi Marky')

Comment

One of the things I love about my career is that it is an “always learning” experience for me. There are times like this where you think to yourself that is an awesome idea and you are very happy to learn it – and at the same time feel so stupid that you didn’t think of it yourself before 🙂

Advertisements

EXTJS in XPages #17 – Manually Updating row data via REST

In this article I will demonstrate how we can use the same REST service we used to populate our grid, to accept an update to one of the row values.

EXTJS in XPages series
Here are links to all of the previous articles in this series

Demonstration
The EXTJS in XPages demonstration database can be found at http://demo.xomino.com/xomino/Extjs.nsf/xContextMenuRESTUpdate.xsp

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

Introduction

In the last article we saw how we can easily create a context menu and in this article we are going to add the icing on the user experience – updating the data on the server via a REST service. Once again in this incremental series, this is a small mount of additional effort to effect a large benefit for the end user experience.

r1

How does that work?

In the XPages world we are able to update a document via a REST service using the URL format:

  • /path/database.nsf/rest.xsp/restPathInfo/UNID

To that URL we have to post (or in this case PUT) a JSON string in the format {“field”: “value”, “field2”: “value2”}

To achieve this we have to “handle” the context menu in the following manner:

var approveAction = Ext.create('Ext.Action', {
    icon   : 'images/add.png',  // Use a URL in the icon config
    text: 'Approve Users',
    disabled: false,
    handler: function(widget, event) {
        var selection = Ext.getCmp('myPanel').getSelectionModel().getSelection()[0];
        buildSubmit.updateREST(selection, "Approved")

    }
})

In this handler we get the selected document, and pass that “selection” and the status value to be updated to the buildSubmit.updateREST function.

Within the function we:

  • Build the JSON data string
    • {‘unid’: selection.data[“@unid”], ‘status’: status}
    • The @UNID is pulled from the REST service property of this document
    • The status is passed into the function “Approved”
  • Pass the string array to the updateStatus function which in turn
    • Creates an AJAX “PUT” request to the REST service
      • xRestService.xsp/byFirstNameFlat/’+data.unid
    • If this successfully updates we then update the row field value with the “Approved value”
var buildSubmit = {

	updateREST: function(selection, status){
		var data = {'unid': selection.data["@unid"],
					'status': status}
		buildSubmit.updateStatus(selection, data)
	},
	updateStatus: function(selection, data) {
		  $.ajax({
			   type: 'PUT',
			   contentType: 'application/json',
			   url: 'xRestService.xsp/byFirstNameFlat/'+data.unid,
			   dataType: "json",
			   data: JSON.stringify(data),
			   success: function(response, textStatus, jqXHR){
				  //update the cell in the grid
				  selection.set("status",data.status);
				},
				error: function(jqXHR, textStatus, errorThrown){
					   console.log('sort order error: ' + textStatus);
				}
		  	});
	}
}

When the data is successfully updated in the grid the EXTJS code automatically adds a marker which indicates that the contents of this field have been changed

r2

Looking at this through Firebug

As we can see from the image below – the “PUT” request is sent to the URL
http://copper.xomino.com/xomino/Extjs.nsf/xRestService.xsp/byFirstNameFlat/B73326B7CDBEFAD385257B1B00254C58
and the JSON is successfully sent to the REST service.

r3

 

Conclusion

There are a few of things to think about in this example:

  1. Once again a few simple lines of code provides a huge benefit to the user.
    1. Being able to update data directly from the grid means they can get their job done faster
    2. Being able to update via a right click action menu means they can get their job done faster
  2. Being able to update the grid field values programmatically means we do not have to reload the grid data
    1. If this is the only point you take away form the this article this is the most important.
    2. Reloading grid data has a cost, in data consumption on the server, time expended to download, time to redraw and most importantly time wasted for the user getting a confirmation that their data was updated. If you have a large number of data rows and the update is in the order of seconds then the user has to wait. Updating a single field of data is instantaneous and the user experience gratification is immediately satisfied.
    3. If you are updating data within the grid itself – DO NOT reload the grid data from the server – EVER – plan your user experience better !
  3. Using the jsonviewservice out of the box EXTLib REST service allows me to make field updates to the database with a tint data footprint.
  4. I cannot understate how awesome #3 is !

typeof() – lifesaving debugger tool

OK so maybe life saving is a little stretch but in this article I will illustrate an occurrence of where using type of() helped solve a real world problem.

The Problem

I wanted to display a data source within a repeat control – relatively simple situation I guess but my brain was asleep that particular morning. I wanted to display the repeat control value within a computed text control……seemed simple

	<xp:this.data>
		<xp:dominoView var="viewSortOrder" viewName="vwSortOrder"></xp:dominoView>
	</xp:this.data>
<xp:repeat var="repeatSortOrder" id="repeat1" value="#{viewSortOrder}" disableOutputTag="true">
	<xp:text tagName="li" escape="false" disableTheme="true">
		<xp:this.value><![CDATA[#{javascript:var temp=""
		temp=temp+repeatSortOrder.title //here is the repeat
		.......
	</xp:text>
</xp:repeat>

Unfortunately this created an error 500 – one of the most useless errors known to XPages but I knew something was wrong…but what?

The answer

Then to my rescue comes “Super TroyReimer” who is of course one of the smartest people I know and he says “oo I dunno let’s find out”

“Why don’t we start by finding the typeof for the repeatSortOrder”?

“errrrrr ok *shaking head and humoring MrSmartGuy*”

so I ran a quick print to the screen and look what came back?

println(typeof(repeatSortOrder))

ty1

Oh well DUH Marky……

The repeat control in this context is running a notesViewNavigator in the Java code (I knew that) and therefore in the computed context it is returning a notesview entry every time the repeat cycles….

So in that case the solution is not repeatSortOrder.title is it repeatSortOrder.getColumnValue(‘title’)

	<xp:this.data>
		<xp:dominoView var="viewSortOrder" viewName="vwSortOrder"></xp:dominoView>
	</xp:this.data>
<xp:repeat var="repeatSortOrder" id="repeat1" value="#{viewSortOrder}" disableOutputTag="true">
	<xp:text tagName="li" escape="false" disableTheme="true">
		<xp:this.value><![CDATA[#{javascript:var temp=""
		temp=temp+repeatSortOrder.getColumnValue('title') //here is the repeat
		.......
	</xp:text>
</xp:repeat>

and that works just fine

Conclusion

Troy Reimer is a very smart person

We all have brain farts and I hope you are as lucky to work with some really smart people like I am

typeof() is a very smart way of figuring out why your object is not what you think it is in SSJS

Why “we need to reduce clicks” is sometimes a fallacy.

In this article i will discuss why I believe the phrase “we need to reduce clicks” is a fallacy (at times).

Introduction

The plain statement of “we need to reduce clicks” really does not tell the whole truth about an application when it comes to modernization. I believe the true statement is that “We need to reduce clicks that annoy us“. There is a very clear distinction in my mind and I will try and explain.

It is all about design and the user experience.

When a user has to select a piece of information and then “action” it, that is probably at least two clicks.

Two clicks is two clicks right?

In the first scenario (lets say) – Looking at a view of data which needs actioning (circa 1998) to get the job done the user needs to:

  1. Click on the URL to open the document
  2. Moves their mouse to, and click the “Approve” butto

In the second scenario (circa 2005) a user is able to:

  1. Select a checkbox next to a list of document
  2. Move their mouse to and click the “Approve” button

In the final scenario (2013) the user:

  1. Right clicks on a document
  2. Selects “Approve” from the context menu with only a minor mouse movement

In all of these situations the user only has two clicks – but in a decreasing level of annoyance the User Experience gets better and better.

I grant you that if the user has to:

  1. click on a document
  2. click the edit button
  3. change the status combo to edit
  4. click the save button

that is two clicks too many and we could reduce the number of clicks (but how long does that REALLY take? less than 30 seconds at best).

But is too many clicks really the problem? No I don’t think it is. The problem lie at the heart of efficiency, expectation and patience.

So what’s the real problem here?

The user perceives that they only has a fixed amount of time to dedicate to “having to do their job” and if Approval of something is their responsibility they want to get it done as quick as possible. In today’s business when local LANs are lightning fast the pages refresh quickly. Time itself to do the approval is in the order of seconds, even in the worst case scenario.

But the perception of time taken to achieve the necessary “action” is verbalized as “need to reduce the number of clicks”.

I believe it is better verbalized as “need to reduce the number of clicks that annoy me”, which translates to “make me the least annoyed to have to do this.”

Conclusion

“Reducing the number of clicks” is sometimes a fallacy, improving the user experience of the same number of clicks they have to do to get their job done should be the real goal.

Bonus Scenario

Just for giggles…

One actual way of reducing the number of clicks by 50% (from two to one) would be to add “drag and drop” to the document and have the user drag the document to an “Approved” landing area – I am not sure that would improve the experience though.

EXTJS in XPages #16 – Right Click Context Menus

In this article I will discuss and demonstrate how to create a context menu so that when a user right clicks on  grid they are able to take action.

EXTJS in XPages series
Here are links to all of the previous articles in this series

Demonstration
The EXTJS in XPages demonstration database can be found at http://demo.xomino.com/xomino/Extjs.nsf/xBufferedRendererContextMenu.xsp

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

Introduction

Context menus (right click menu) are one form of user experience which “reduces clicks”. But that is a fallacy in some senses because the user is not clicking any less they are just less annoyed by the clicks 🙂

If the user is in a grid and has to select some documents, then move their mouse out of the grid and click an “Approve” button that is “one click” but “with an annoying mouse movement”. Right click – select Approve is actually two clicks…..which is more clicks but is less annoying because of little to no mouse movement.

r1

How does that work?

In the grid we are able to create a context menu by “Creating a menu” and “adding it to the onContextMenu event of the grid view. In the example below I am creating the “approveAction”. In that action we:

  • define the icon which will appear int he menu
  • define the text on the menu action
  • have a handler which interprets what happens when the menu action is clicked
    • In this case the action gets all the selected documents and creates a JSON string from it
    • That JSON string is then shown on the console for debugging
var approveAction = Ext.create('Ext.Action', {
    icon   : 'images/add.png',  // Use a URL in the icon config
    text: 'Approve Users',
    disabled: false,
    handler: function(widget, event) {
        var selection = Ext.getCmp('myPanel').getSelectionModel().getSelection();

    	var theJSON=[]
    	for ( var i = 0; i < selection.length; i++) {
    		theJSON.push({'unid': selection[i].data["@unid"],
    						'field': 'status',
    						'val': 'approved'}
    		)
    	}
    	console.dir(theJSON)
    }
})

I will get to writing about how to do the update like I promised……but suffice to say that this is posted at a customREST control which will then update the values based on the UNID.

r2

The menu is added to the grid in the following manner. A view listener is added to the grid and in the itemcontextmenu event we detect where the event is happening and then display the menu at exactly that point (you could offset this if you wanted)

The menu is smart enough that if you do not click on the menu itself it disappears when you click away from it.

gridFunc = {
    self: this,
    grid: function(){
	  return Ext.getCmp('myPanel')
    },
    contextMenu: Ext.create('Ext.menu.Menu', {
        items: [
                approveAction,
                rejectAction
        ]
    })
}

var grid = Ext.create('Ext.grid.Panel', {
    	viewConfig: {
    		stripeRows: true,
	        listeners: {
	            itemcontextmenu: function(view, rec, node, index, e) {
	                e.stopEvent();
	                gridFunc.contextMenu.showAt(e.getXY());
	                return false;
	            }
	        }
	    },
etc

Reusing the actions

Not only can the actions be added to the context menu, but because they are created as EXT Objects they can be added to other place as well – like a toolbar

	gridDock = [{
		id: 'activeStorePaging',
	     xtype: 'toolbar',
	     dock: 'bottom',
		 items: [
		   {
		       text: 'Clear Filter Data',
		       handler: function () {
		           grid.filters.clearFilters();
		       }
		   },
		   '-',
		   approveAction,
		   '-',
		   rejectAction
	    ]
	 }]

r3

Conclusion

As you can see with great control and little additional effort (as ever!) we are able to add more feature rich functionality to our grid

EXTJS in XPages #15 – Multi-select of documents

In this article I will discuss and highlight “multiselect” within the extjs grid. With a two line addition to the grid code we are able to select multiple documents and then action them.

EXTJS in XPages series
Here are links to all of the previous articles in this series

Demonstration
The EXTJS in XPages demonstration database can be found at http://demo.xomino.com/xomino/Extjs.nsf/xBufferedRendererCheckBox.xsp

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

Introduction

To be able to action more than one document we want to be able to select more than one in the grid. There are two ways we can do this and we are going to look at both. In the next article we will look at how to affect change to multiple documents.

mul1

How does that work?

Within the grid configurations we are able to add a multiSelect  parameter which allows us to select multiple documents. This can be achieved in the same way as you would multiselect in excel:

  • Hold down the SHIFT key and click two documents, Everything inbetween will be selected as well as the two documents clicked
  • Hold down the CTRL key and the documents clicked will be selected in addition to each other
  • Single clicking any other document without either key held down will only select the document clicked

mul2

  var grid = Ext.create('Ext.grid.Panel', {
        renderTo: 'gridHere',
        frame: true,
        multiSelect: true,     // <-----------
        height: 400,
        title: 'Users',
	    plugins: buffRend,

As you can see from the above picture the multiple selected documents are all highlighted.

Going one step further (if the users prefer it) you can add a “checkboxmodel” parameter to add “checkboxes” to the grid

  var grid = Ext.create('Ext.grid.Panel', {
        renderTo: 'gridHere',
        frame: true,
        multiSelect: true,     // <-----------
        selType: 'checkboxmodel',  //<----------------
        height: 400,
        title: 'Users',
	    plugins: buffRend,

Not only does this give a new interface it also allows users to select and deselect all by clicking on the checkbox at the top of the grid

mul3

Conclusion

Adding multi-select support to the grid is very simple and in the next article we will look at how to take action on that

EXTJS in XPages #14 – Grid editing and saving data via REST CRUD

In this article I will demonstrate the ability to edit data within and EXTJS grid and have the changes saved directly to the domino database via the same REST service from whence they came.

I have to say a big thank you to Steve Zavocki who actually found my old failed attempt to make this work in my demo database. It was unblogged because I couldn’t get it to work – well that was in April, he asked some questions about it and I came back to it and with another 4 months of EXTJS experience under my belt I figured it out in no time. Without the prompting it might have sat there for another 4 months untouched.

EXTJS in XPages series
Here are links to all of the previous articles in this series

Demonstration
The EXTJS in XPages demonstration database can be found at http://demo.xomino.com/xomino/Extjs.nsf/xRESTCRUD.xsp

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

Introduction
This is really where everything starts to come together and we begin to be able to create functionality customers have always been asking for. Being able to update the data in a view without having to:

  • go to the view
  • go into a document
  • edit it
  • change it
  • save it
  • go back to the view

The Row Editor plugin
The row editor plugin is very easy to add to your grid – you create the variable for the rowEditor (taken from the website example)

 var rowEditing = Ext.create('Ext.grid.plugin.RowEditing', {
 listeners: {
	cancelEdit: function(rowEditing, context) {
	// Canceling editing of a locally added, unsaved record: remove it
	 if (context.record.phantom) {
		store.remove(context.record);
	 }
	}
 }
 });

and you add it to the grid in the same way as other plugins

	var grid = Ext.create('Ext.grid.Panel', {
	renderTo: 'gridHere',
	plugins: [rowEditing], // <----------
	height: 400,
	frame: true,
	etc.....

and as simple as that you can now double click on a grid and edit the columns!

rc1

Making it communicate with the REST service

In this example I am using a store with a proxy type of REST. The EXTJS grid already understands this is a REST service and does most of the heavy lifting by itself. For more information on how a REST service works in Domino then check out Chris Toohey’s article 

There is also more documentation on the XPages REST here http://www.openntf.org/blogs/openntf.nsf/d6plinks/NHEF-93YBTB

var store = Ext.create('Ext.data.Store', {
        autoLoad: true,
        autoSync: true,
        model: 'Person',
        proxy: {
    		pageParam: 'p', // <---important to add
            type: 'rest',      // <---- type of proxy is REST
            url: 'xRestService.xsp/byFirstNameFlat2',
            reader: {
                type: 'json',
                root: 'data'
            },
            writer: {
                type: 'json'  // <- data is written back in JSON format
            }
        }
    });

As you can see it also defines the write format back to the REST service. When you click on the UPDATE button on the row editor a URL is generated and data is sent. It does this via a “PUT” request which only sends a small amount of data to the server.

rc2

Enabling a PUT request

By default PUT is not enabled on a Domino server and you can enable it by turning on domino data services on the server or by adding it to the allowed HTTP methods on the web site document in the Address Book

For more information on that check out this blog article by Michael Brownlee

Domino REST service

The REST service URL which generates the data looks like this

http://demo.xomino.com/xomino/Extjs.nsf/xRestService.xsp/byFirstNameFlat2?_dc=1375392340494&p=1&start=0&limit=25

The REST service POST look like this

http://demo.xomino.com/xomino/Extjs.nsf/xRestService.xsp/byFirstNameFlat2/66F3A5FE2B2A38CC85257B1B00254E6D?_dc=1375392353545

As you can see the UNID has been added to the URL after the byFirstNameFlat2/
The _dc= is a random number generated by the grid

It is important to note that the grid automatically adds a “page=1” parameter to the REST service call and for some reason I cannot fathom this makes the REST service fail!

byFirstNameFlat2?_dc=1375392340494&page=1&start=0&limit=25 —-> FAIL

so I added the pageParam to the store proxy config and told it to use “p” instead – “p=1” apparently means nothing to a REST service and we all carry on happily….

and the UNID comes from where exactly?

So the other thing the grid does is by default it assumes that you want to send back the “id” field to the REST service and appends the “id” field onto the end of it

byFirstNameFlat2

becomes

byFirstNameFlat2/66F3A5FE2B2A38CC85257B1B00254E6D

To do this I added the UNID as a field in the REST service and added that as a hidden column in the grid.

Ext.define('Person', {
    extend: 'Ext.data.Model',
    fields: ['firstname', 'lastname', 'address', 'city', 'state', 'id']
});

 

        columns: [{
            header: 'id',
            sortable: true,
            dataIndex: 'id',
            hidden: true
        }, {

“id” is not the only field name which can be used as there is another proxy parameter called “idParam” similar to the pageParam which will allow you to name the “unid” field however you like – but the grid must know which field in the grid is the UNID and which it should throw back at the REST service

And that is pretty much it – you can download the sample database and take a look at the code for yourself.

If you want to get really fancy (and we will in the next blog article) you can also edit numbers/date fields and add validation as well to ensure that your users and not submitting rubbish back to the REST.

If you want to be uber secure as well there is always the querySaveDocument event on the REST service which you could also use to validate the incoming data.

rc3

Conclusion

In this article we have seen how we can easily edit data directly in the data grid and from that we can update our domino data with a tiny http post back to the server. This is good for your network traffic and fantastic for your user experience.

No XPage controls were harmed in the creation of this demonstration