Friday, August 23, 2013

view.postScript() only works for certain events on an Xpage

I discovered an undocumented feature today when trying to use the view.postScript() method.  This method (added in 8.5.3) allows you to run client side JavaScript at the conclusion of a block of server side JavaScript.  I discovered this issue, when I moved some code in the onClientLoad event of a Panel from clientside to serverside.   I needed to reference a value on the current document, and run a different function based on the value.

I thought to myself, "no problem, I will just use view.postScript() and it will run fine."   When I test the code, nothing happens.   The script never runs, and there are no errors.  I checked Firebug and the scripts loaded fine, as in I was able to launch them using the JavaScript console.  At that point, I did some googling and didn't find all that much out there on view.postScript().   I found a few articles, but nothing that helped me with my problem.

I then turned to Stack Overflow.  Sometimes, just the act of trying to write a question as clear as possible will cause me to think of the answer before I even post the question, but not this time though.  I went ahead and posted this question.

After I posted I got to thinking that maybe it wasn't my logic, but just that the code was being ignored.   I created a alert message using view.postScript and it still did not work.   I then tried putting that alert in the afterRenderResponse event of the xpage, where I had some other code.   Nothing happened there either. Frustrated, I then started putting the code in all the events, and to my surprise it worked some places and not others.  

Basically, the view.postScript() method only works in certain events.   I do not know why this is, but if I had to guess it has something to do with the JSF lifecycle.

I did some experimenting with the Xpage events and here is what I found.

  1. onClientLoad = nothing
  2. beforePageLoad = XSP error
  3. afterPageLoad = WORKS!
  4. afterRestoreView = nothing
  5. beforeRenderResponse = WORKS!
  6. afterRenderResponse = nothing
I can tell you that it doesn't work in the only Panel event, onClientLoad.  I am pretty sure I have used it with onClick events before, so it does work there.

The bottom line is when you view.postScript(), and you should, be aware that, if it does nothing then you might be using it in a place where it isn't supported.   In that case, first try a simple alert, and if that works then it is your code, if it doesn't then you can't use view.postScript in that event.   In my case, I moved the code to afterPageLoad and it worked great.

Monday, August 19, 2013

Managing Unwieldy URL's in Xpages

I wanted a way to control the URL's in the large Xpages application I am developing.  The application is customer facing after an authentication process.

My goals were to:

  1. Simplify the unsightly URL's 
  2. Prevent the URL's from revealing the presence of a second database
  3. Prevent the user from using bookmarks or the address bar and jumping into the middle step of a multistep process
I was able to accomplish the first two of these goals and this post will explain who I accomplished them.  The third goal is still outstanding.  I will make a follow up post when/if I figure that one out.

Before:   

This was the URL to open a document prior to making the changes outlined here:
http://notesdev1.my_company.com/po/po.nsf/%24%24OpenDominoDocument.xsp?databaseName=CN=My_Company_NotesDev1/O=MyDomain!!PO%5CPO-data.nsf&documentId=E879C68A9A88F6DD87257BC6005A0748&action=editDocument

This URL is very unsightly, and shows that I keep my data in a separate NSF.   It is also extremely long.   

After:

This URL does the same as the first and opens the same document in the same database
http://notesdev1.my_company.com/po/po.nsf/New_PO.xsp?doc=E879C68A9A88F6DD87257BC6005A0748

Notice that the URL does not reveal the location of the data, or my companies Notes domain.

The Remedy:

Before I explain here what I did, let me say that I would not have been able to come up with this without the help of Stephan Wissell and Per Henrik Lausten via Stack Overflow.   Here is the link to my question.   

The fix is two part.  

First, you have to manually build your URL in the container where you display your documents.   In my case, I am using a view control, which is really a placeholder until I decide how I want to display the data.   These instructions might have to modified for other containers.

A)  Give the View control a value in the 'var', I used 'viewData'
B)  In the onClick event of the column that is set to display as link, add the following code

var xpage = "New_PO.xsp"
var unid = viewData.getDocument().getUniversalID()
facesContext.getExternalContext().redirect(xpage + "?doc=" + unid);

The result of this is the after URL listed above.

The second part is to read the shortened URL:

In the Xpage Properties under Data, do the following:

A) Create your data source as you normally would
B) Specify the location of your data whether it is in the current NSF or another.  I prefer to separate data and design.
C) Change the 'Default action' to Open Document, or Edit Document
D) In the 'Document id' calculate the value to return param.get("doc") which grabs the UNID  that you passed

The result is that you have a nice looking URL and you can bind to any database without revealing it through the URL parameters.  I have read that you can accomplish similar results using site documents, but I wanted a solution that works whether your administrator is friendly or not. 


Thursday, August 8, 2013

Creating Tooltips in Bootstrap - Two Different Styles

In this post, I am going to go over the two different ways to make tooltips in Xpages using Twitter Bootstrap.

Background

I was wanting to create tooltips to shorten my labels and streamline a crowded window with many input fields in the application I am building.   After a quick search, I found an article that Kathy Brown wrote on how to create tooltips.   Within a few minutes I had a working tooltip, but I wanted tooltips that looked like what I saw on the Bootstrap site.
I wanted my tooltips to look like this
I tried a changing attributes using and couldn't do anything to change the default behavior.  I did remember to add 'data-' before the attributes.   After some searching and the help of jsfiddle, I was able to figure how the easiest way to create tooltips how I wanted them.

Option 1

This way is extremely simple and quick to implement.   In my case, I wanted to the tooltips to be on the labels.  You will notice that I am using a Bootstrap feature where I combine my input field with a label together using what is called an "input-prepend".   The label itself is wrapped in a <span> with a class of "add-on".   I wanted my tooltip to act on the label text in the add-on.   If you want the tooltip to act up on a control such as a button, you will want to add a new attribute (attrs) in All Properties of the control.

  1. Wrap your text in a span if it is not already OR Add a new attribute to control you are using.   In my case I am writing the html directly in the source.
  2. Add a property called "data-toggle" with a value of "tooltip"  See Update below
  3. Add a property called "title" with the text you want in the tooltip
Example:  <span class="add-on" data-toggle="tooltip" title="Seventh Street">02</span>

You will get a result like you see in the screenshot below.  The tooltip shows up in relation to the mouse location, always in the lower right.  You cannot control the location, or the delay.

The result of Option 1
UPDATE:  Upon further testing, I think a correction is in order.  Option 1 is not a bootstrap feature at all, but is part of HTML 5.   All you need is the "title" attribute to make this work.   The "data=toggle" does nothing.   

Option 2 is the Bootstrap way of writing tooltips.

Option 2

This is the way that tooltips are shown on the Bootstrap site, the first option isn't mentioned on the current site.  The first option would be fine and looks decent, but I wanted a little more control over placement and timing.

  1. Add a html property called "title" with the text of the tooltip.  (No need for 'data-toggle' with this option)
  2. If you already have a "class" attribute then add a second one separated by space.  Name it what you want, except don't use "tooltip" or it won't work.  I called mine "tips".  If you don't have a "class" attribute then create one.
  3. Add code similar to the following in your $(document).ready(function(). If you don't already have one that add one to the onClientLoad event of your Xpage, make sure you choose Client!
  4. Customize the tooltips however you want, using the bootstrap site as your guide.  In my case, I made the delay one second, and the placement always at the bottom.  With this option, the tooltip placement is based on the control it is attached to, and not the mouse location within the control.
Example:  <span class="add-on tips" title="West">01</span>
Code to enable the tooltip (Line 2 is unrelated the tooltip)

The result of Option 2

Wednesday, August 7, 2013

The Benefits of Using a Validator in XPages over using the rigid Validators

I know the Post Title might be a bit confusing, but so is the subject I am going to talk about.   The reason I am making this post is really for me, so that I don't forget how to do this.  I hope that it also might benefit others as well.   The reason that this is a confusing subject is that there is a "validator" property, and "validators" with the S that also do similar things.

Two months ago, I posted a question on Stack Overflow about trouble getting the xp:customValidator to work.   The answer that worked for me was by Paul Withers, thank you Paul if you ever read this. (My code below is a derivative of Paul's.)

Yesterday, I trying again to use one of the validators and getting frustrated with the result.  It was then that I remembered the better way of using "validator".  I have found that when I blog about something, then I own it, and am far less likely to forget how to do it.  In this post I am going to explain why I think using the validator is the best way to validate user input.

My issue started because I did not like the fact that my input fields would default to zero when bound to a managed java bean field of type double or int.   I decided that I would change the type in the bean to String, and then change my methods to use Double.parseDouble() to first convert the value to something that I can perform math on.   (If there was a better way to avoid the zero then I would like to know, please comment below)

To make this work, I first needed a xp:converter that converts the user input to String.

Without this converter you would get an exception when entering a numeric value.
I then needed to make sure that the user actually entered a number, and in my case that that number was not negative or a fraction.  This is the code I used in the validator property.

Validator code

Code Explanation

  • 'value' is something you get for free.  It is value the user entered.  I found this poorly documented.
  • The order matters, you will want to arrange the validators from general to more specific.
  • You have total control over the error message that you present to the user
  • Number() is a built in javascript function that converts a string to a number, it returns NaN if it cannot be converted.
  • The line that begins 'var msgObj' is long and includes the following line.
  • The line: this.setValid(false) prevents the submit.
  • I could combine these three statements into one message if I choose, but I prefer to give the user a message that tells them exactly what they are doing wrong.


So why use the Validator over the preset Validators

  • More Control:  You can make them fire in the order that makes sense to you.
  • You can show whatever error message you want.   The preset Validators sometimes decide for you.  In one case, it ignored the 'message' I put in, and still gave it's own.
  • You have unlimited flexibility in the conditions you want to validate.
  • The customValidator under the validators in unreliable and shouldn't be used IMO
NOTE:  For some reason you cannot recreate a validator that does the same thing as validateRequired.  You will have to use the global function.  They work together just fine.




UPDATE:

I didn't mention that with my examples, but I was also using a xp:validateRequired.  I didn't think to mention it.  I have discovered that when I remove that, the validator never fires when using Chrome.  It works fine on Firefox and IE, but not in Chrome.  If anyone has any clues why, then please comment or contact me directly at zavocki@hotmail.com.

The fix for this was to remove type="number".  It has something to do with the nice helper arrows that Chrome presents.  I would like to figure out how to use this attribute so mobile users see the right keyboard, perhaps some jQuery is in order.