Accessing the original element when using Select2-focus

In the application we are currently working on I wanted to add an ajax call to a JSON service, but only for certain fields. Rather than go through the application and add the code to every element I used a delegated focus event for the field with an attribute of  “help_fieldName”. The following HTML represents the code on the form:

          <div class="col-lg-8">
            <select help_fieldname="territory">
              <option value="UK">UK</option>
              <option value="US">US</option>
              <option value="Global">Global</option>
            </select>
          </div>
          <div class="col-lg-4">First Name</div>
          <div class="col-lg-8">
            <input type="text" class="form-control" help_fieldname="firstname" />
          </div>
          <div class="col-lg-4">Last Name</div>
          <div class="col-lg-8">
            <input type="text" class="form-control" help_fieldname="lastname" />
          </div>

The following jQuery code makes it work. The getHelp function shows/hides the help based on the boolean in the function.

$(document).ready(function(){
	$('body')
		.on('focus', '[help_fieldName]', function(){
		     getHelp(true, $(this))
		})
		.on('blur', '[help_fieldName]', function(){
		     getHelp(false, $(this))
		})
	})

 

What I found though was that when I used Select2 the field attribute was not being picked up. This is because Select2 creates it own DOM elements to display the field and the original field attributes are not transferred to this new DOM structure.

So I needed to come up with a way of accessing the original element from the select2. This turned out to be very simple – select2 emits an event on focus and blur called select2-focus and select2-blur. The event target gives me direct access to the original field the select2 is representing.

Using this simple modification to the delegation code I was able to access the help_fieldName to pass to the help Function. The image below shows the console.log, highlighting the underlying DOM element.

$(document).ready(function(){
	$('body')
		.on('focus', '[help_fieldName]', function(){
		     getHelp(true, $(this))
		})
		.on('blur', '[help_fieldName]', function(){
		     getHelp(false, $(this))
		})
		.on('select2-focus',  function(e){
		     console.log(e.target)
		     getHelp(true, $(e.target))
		})
		.on('select2-blur',  function(e){
		     getHelp(false, $(e.target))
		})
	})

s1

Advertisements

jQuery Promises – Taking action .when() multiple ajax calls are complete

jQuery.when() – “Provides a way to execute callback functions based on one or more objects, usually Deferred objects that represent asynchronous events”

https://api.jquery.com/jQuery.when/

Wouldn’t it be nice to be able to know when two ajax calls are both complete – and then process the data returned from both, at the same time ?

Callbacks

Off the top of my head I would normally achieve this using a callback – and I have done so in some of my ExtJS in XPages articles. The following code snippet creates a callback that once the $.ajax call the url is complete take the data and pass it to the createGrid function. In there I could make another ajax call and then when that is complete I could process both data sets.

function getGridData(url, callback){

	var dataURL = url
	$.ajax({
		  url: dataURL+"&rand="+aRandomNumber()+"&"
		}).done(function ( data ) {
			//one the data is loaded then pass it to the callback function
			//passed in as one of the function arguments
			if(callback){
				callback(data)
			}
		});
}

var url = location.href
url = (url.indexOf('5Col')>-1) ? '5Col' : ""

url = 'xRestService.xsp/byFirstNameFlat'+url+'?count=100000'
var theData
var theCallBack = function(theData){
		createGrid(theData)
		}
getGridData(url, theCallBack)

This kinda makes an synchronous linear usage of asynchronous calls – do this then do that

As inelegant as that it, it works. The problems really start when you have 3, 4 or more callbacks and you get a callback hell of what if something fails in the middle and you have to run back up the tree looking for a fail – not pretty.

Using jQuery.when()

Modern browsers support the Promises concept which natively allows us to do the same thing but we know what that means and not the browsers we code for today.

.when() gives us the ability to pass as many ajax calls as we like, and then create one line of code which processes all the answers together.

Example

Here is a simple REST service feed from an XPages URL (fakenames data)

http://copper.xomino.com/xomino/extjs.nsf/xRestService.xsp/byFirstNameFlat?count=5

[
  {
      "@entryid":"1-AE3FB4C32057B87C85257C22000A83DD",
      "@unid":"AE3FB4C32057B87C85257C22000A83DD",
      "@noteid":"F5CA",
      "@position":"1",
      "@siblings":1304,
      "@form":"fUserName",
      "noteid":"NT0000F5CA",
      "firstname":"Alfred",
      "lastname":"Hall",
      "address":"2139 Jail Drivedd",
      "city":"Tremont",
      "state":"FL",
      "zip":615680,
      .........
  }
]

Which is nice ‘n all, but if we then wanted to know what State FL was (remember this is an example)

At the same time as pulling in the person you could also pull a list of US states

http://copper.xomino.com/xomino/extjs.nsf/states.json

{
    "AL": "Alabama",
    "AK": "Alaska",
    "AS": "American Samoa",
    "AZ": "Arizona",
    "AR": "Arkansas",
    "CA": "California",
    "CO": "Colorado",
    "CT": "Connecticut",
    "DE": "Delaware",
    "DC": "District Of Columbia",
    "FM": "Federated States Of Micronesia",
    "FL": "Florida",
    "GA": "Georgia",
    ......

From that we can then do a cross reference and display something useful.

The $.when returns each element as an Array of data so we have to get the zeroth value of each object returned to access it’s key/pairs

$.when(
    $.ajax({
        url: "xRestService.xsp/byFirstNameFlat?count=5",
        dataType: "json"
    }),
    $.ajax({
        url: "states.json",
        dataType: "json"
    })
).done(function( people, states ) {
    for (var i=0; i < people[0].length; i++){
        var person = people[0][i]
        var temp = ""
        temp=temp+person.firstname+" "
        temp=temp+person.lastname+" lives in "
        temp=temp+states[0][person.state]
        console.log(temp)
    }

});

As you can see from the screenshots below – the ajax calls can be executed in either order and still the result stands because the .done() function is not run until both are complete

d2

d1

d3

 

 

Conclusion

This is a very interesting capability which has apparently been around for ages and I wish I had known about it earlier (added jQuery v1.5)

 

PATCHing a Document using Domino Data Service

Talk about frustrating – in a week full of slow progress and CORS cross domain hell I found this little annoyance after hours of staring and curing – once again the power of trial and error triumphs over my stupidity again.

The problem

I have a Domino Data Service running on the server and I want to update a document

This is the imaginary URL

https://xomino.com/issues.nsf/api/data/documents/unid/FDCA9C28A793D3F785257C4D0068BBCA

returning the imaginary data just fine

{
    "@href":"\/issues.nsf\/api\/data\/documents\/unid\/FDCA9C28A793D3F785257C4D0068BBCA",
    "@unid":"FDCA9C28A793D3F785257C4D0068BBCA",
    "@noteid":"6EC6",
    "@created":"2013-12-26T19:03:58Z",
    "@modified":"2014-03-23T16:40:10Z",
    "@authors":
    ["CN=Marky"],
    "@form":"question",
    "MIME_Version":"1.0",
    "score":0,
    "answers":1
}

and I am trying up to update it using a simple AJAX call

$.ajax({
  data : {'marky':'newfields'},
  type: 'PATCH',	
  contentType: "application/json", 
  url:'https://xomino.com/issues.nsf/api/data/documents/unid/FDCA9C28A793D3F785257C4D0068BBCA',
  }).done(function(data){
    console.log(data)
  }).error(function(data){
    console.log(data)
  })

Unfortunately I get this big old ugly error….
{
“code”:400,
“text”:”Bad Request”,
“message”:”Error while parsing the JSON content”,
“type”:”text”,
“data”:”com.ibm.domino.services.ServiceException: Error while parsing the JSON content\r\n\tat com.ibm.domino.das.resources.DocumentResource.updateDocumentByUnid(DocumentResource.java:278)\r\n\tat com.ibm.domino.das.resources.DocumentResource.putDocumentByUnid(DocumentResource.java:184)\r\n\tat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\r\n\tat sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:60)\r\n\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37)\r\n\tat java.lang.reflect.Method.invoke(Method.java:611)\r\n\tat org.apache.wink.server.internal.handlers.InvokeMethodHandler.handleRequest(InvokeMethodHandler.java:63)\r\n\tat

bunch – o – useless Java Stack Trace which never helped anyone…….

com.ibm.designer.runtime.domino.adapter.LCDEnvironment.service(LCDEnvironment.java:306)\r\n\tat com.ibm.domino.xsp.bridge.http.engine.XspCmdManager.service(XspCmdManager.java:272)\r\nCaused by: com.ibm.commons.util.io.json.JsonException: Error when parsing JSON stream\r\n\tat com.ibm.commons.util.io.json.JsonParser.fromJson(JsonParser.java:86)\r\n\tat com.ibm.domino.das.resources.DocumentResource.updateDocumentByUnid(DocumentResource.java:271)\r\n\t… 65 more\r\nCaused by: com.ibm.commons.util.io.json.parser.ParseException: Encountered \” \”approved \”\” at line 1, column 1.\r\nWas expecting one of:\r\n \”false\” …\r\n \”null\” …\r\n \”true\” …\r\n <INTEGER_LITERAL> …\r\n <FLOATING_POINT_LITERAL> …\r\n <STRING_LITERAL> …\r\n \”{\” …\r\n \”[\” …\r\n \”(\” …\r\n \r\n\tat com.ibm.commons.util.io.json.parser.Json.generateParseException(Json.java:637)\r\n\tat com.ibm.commons.util.io.json.parser.Json.jj_consume_token(Json.java:572)\r\n\tat com.ibm.commons.util.io.json.parser.Json.parseJson(Json.java:409)\r\n\tat com.ibm.commons.util.io.json.JsonParser.fromJson(JsonParser.java:84)\r\n\t… 66 more\r\n”

The solution

So I googled around and I found that the error is caused because the com.ibm.commons.util.io.json.parser is expecting a string as input

http://stackoverflow.com/questions/14254536/is-there-a-way-to-return-a-json-object-within-a-xerestviewcolumn#comment19788635_14254536 – once I found that the solution was simple – send a string and not an object…

$.ajax({
  data : '{"marky":"newfields"}', // Notice this is now a string not an object
  type: 'PATCH',	
  contentType: "application/json", 
  url:'https://xomino.com/issues.nsf/api/data/documents/unid/FDCA9C28A793D3F785257C4D0068BBCA',
  }).done(function(data){
    console.log(data)
  }).error(function(data){
    console.log(data)
  })

PATCHing of a document makes a lot of sense for many reasons – mostly because of reduction in bandwidth – the example code works nicely – but you have to update the web site document to allow the PATCH method.

Be aware that using code like this you are also exposing all your data to having any old fool like me breaking your field based workflow and filling your documents full of crap data if they so chose to.

My suggestion would be to use your own REST service written with your own protections in it 🙂

Once again my stupidity is boundless and as I already discovered on multiple occasions,  JSON is a string and not an object.

 

 

How to add icons to individual items in a Select2 multi-value field

Select2 is one of the best user interface enhancers I have come across – if you do not know what it is then you need to go play with it.

It transforms SELECT boxes into dynamic, stunning, interactive UI elements and allows for all sorts of customizations and developer fun.

What I want to be able to do is select items from different categories within the select2 box and then add an icon displaying to the user which category they came from. In this article I will show how.

Example

I want to take this

s3

turn it into this with a type aheads2

And when a user selects the items – make this based on the category selected

s2

And it is really pretty simple and straightforward.

The Code

We start with our select box

<select style="width:500px" id="vehicle" multiple="multiple">
<optgroup label="Cars">
	<option class="car">Honda</option>
	<option class="car">Toyota</option>
	<option class="car">Ford</option>
	<option class="car">GM</option>
</optgroup>
<optgroup label="Bikes">
	<option class="bike">Harley Davidson</option>
	<option class="bike">Kawasaki</option>
	<option class="bike">Aprilia</option>
	<option class="bike">Ducati</option>
</optgroup>
</select>

and then we add our select2 code

$("#vehicle").select2().on("change", function(e) {
	if (e.added) {
		var icon = ""
		$('.select2-search-choice DIV').filter(function() {
		    icon = '<img src="images/'+e.added.css+'.png"/>&nbsp;';
		    return $(this).text() == e.added.id;
		}).prepend(icon);
      }
})

So what this does is:

  • select the #vehicle DOM element
  • turn it into a Select2 plugin control
  • When the control is changed and if an element is added
  • Find the DIV which has been created to display the new item
    • We use filter based on the newly added.id to make sure we only get the one just created
  • create the HTML for the appropriate icon based on the class of the selected item
  • prepend that icon HTML inside of the DIV containing the newly selected option

Conclusion

I have barely scratched the surface of what is possible with Select2. But I hope that you can see with only 9 lines of code we have significantly improved a user experience 🙂

I love Select2 and the options are endless

Enjoy 🙂

jQuery in XPages #20 – NProgress – YouTube-like slim progress bar

In this article I will describe how to implement and use the jQuery NProgress nano scrollbar plugin

Demonstration

The XPages integration of NProgress is demonstrated here

Download

The demonstration database can be downloaded from the link above or from here

NProgress

Introduction

Since Youtube.com added their nano scroll bar at the top of the page there have been a flurry of different jQuery plugins which mimic the nano progress bar at the top of the screen.

np1

The silly thing is that the progress bar itself does nothing useful.

  • It does not
    • Indicate any meaningful progress on the download of the AJAX or page
    • Give any indication of the likelyhood of page download completion
  • It does
    • Give the user a false sense of something happening on the page
    • Provide a nice User Experience for very little overhead and effort on the part of the developer

Using NProgress you can very quickly and easily add this capability to your website for only a very small overhead to the size of your application.

Adding NProgress to your XPages

  • Go to the NProgress site and download the file from the github site linked on the page
  • Drag and drop the contents of the zip file into your WebContents folder
  • Create a sample page with the following
 <link href='nprogress/support/style.css' rel='stylesheet' />
  <link href='nprogress/nprogress.css' rel='stylesheet' />

<xp:this.resources>
	<xp:headTag tagName="script">
		<xp:this.attributes>
			<xp:parameter name="type" value="text/javascript" />
			<xp:parameter name="src" value="js/jquery.js" />
		</xp:this.attributes>
	</xp:headTag>
</xp:this.resources>

<xp:this.resources>
	<xp:headTag tagName="script">
		<xp:this.attributes>
			<xp:parameter name="type" value="text/javascript" />
			<xp:parameter name="src" value="nprogress/nprogress.js" />
		</xp:this.attributes>
	</xp:headTag>
</xp:this.resources>

The reason we add this to our XPage in the manner above is because nprogress uses AMD as a possible loader (to integrate with require.js) and because of that Dojo 1.8 (on an R9 server) breaks it.

On my demo page I have loaded a viewPanel with 1300 documents in it – I cannot show you more because I do not want a large database on my host’s site but rest assured if you run this up to a 5000 row viewPanel the effect is better because the refresh is slower.

np2

On the demo I have included the main example from the NProgress site and you can see that there are a number of methods for starting and manipulating the progress bar. I use start because it very nicely randomly increments itself to give that illusion of progress.

On the XPage I have a button which refreshes the view Panel – and in the client script as the button is pushed I start the NProgress. In the onComplete of the refresh I end NProgress and really it is as simple as that

<xp:button value="Reload  viewPanel" id="button1" styleClass="clickMe btn btn-warning">
	<xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="viewwrapper"
		onComplete="NProgress.done()">
		<xp:this.script><![CDATA[
			NProgress.start()
		]]></xp:this.script>
	</xp:eventHandler>
</xp:button>

I would suggest that if you are able to integrate with Tony McGuckin’s excellent XSnippet for intercepting dojo io events you could create an automated NProgress bar should you so chose.

Conclusion

As web developers we have used spinners in our application for quite a number of years – this is a new twist on that concept and seems to be getting very good reviews from users. Consider integrating it into your applications.

Using jQuery to remove Dojo CSS from an XPage

I am currently working on a customer application which is oneuiv2.1 themed and I wanted to create a simple jQuery Mobile site. the dojo css was really messing up the styling and just for interest I wanted to see if I could programmatically remove the dojo css from the page client side

The application has the property checked to use runtime optimized JavaScript and CSS. This combines all the dojo css files nicely into one link.

do1

Using a jQuery selector I can get a handle on that <LINK> as follows

$('link[href*="dojo.css"]') //select all LINK objects where the href contains dojo.css

do2

Once I have that I can just remove the element from the DOM like this

$('link[href*="dojo.css"]').remove()

Unfortunately I cannot show you what this does my customer’s site to make it look all pretty and jQueryMobile like, but I can show you how it blows up a oneui site and you can take it from me this fixed my problem.

Just add the above code in a *SCRIPT* tag within your XPage and it will remove the dojo style before it is rendered to the end user – thus they will never know.

http://xpages.info/xpageshome.nsf/Demos.xsp

Before

do3

After

do4

 

Caveat

Yeah I realize this means the CSS file(s) are downloaded and then removed and I am sure there are more elegant ways to handle the issue server-side.

This was an exercise in learning that jQuery can be used to remove entire CSS DOM objects 🙂

Remove all classes using dojo and jQuery

One of last week’s challenges on the side was to take this Connections wiki page and make it readable on a small screen – if you drag the right hand side over to the left you will see that the right navigation menu overlays the non-wrapping text. The main section is forced to a 980px width !

http://www-10.lotus.com/ldd/lcwiki.nsf/dx/Best_Practices__Troubleshooting_Lotus_Connections_3.0#Traces

A very brute force approach to this is to remove all the classes from all the elements on the page

In jQuery you would do this:

$('*').removeClass()

in dojo (which the page is written in) it takes slightly longer

dojo.forEach(dojo.query('*'), function(item, i){
    item.className=""
})

It would be relatively simple to turn this into a bookmarklet to run for yourself

Here is a bookmarklet you can add to do it for you – this will also work on other IBM wiki pages

Create any link on your bookmark bar and then edit it. Copy and paste the following as your URL

javascript: dojo.forEach(dojo.query(‘*’), function(item, i){ item.className=”; })

b1

When you go to the wiki page click the bookmarklet and all classes will be removed – this is only work on pages which have dojo on them already (which IBM documentation does).