Friday, June 28, 2013

Issue with Referencing an Xpages generated #Id using Bootstrap

UPDATED

I am not sure if this is a Bootstrap issue or an HTML5 issue, but there is an issue when referencing the long generated ID files in Xpages.   ID's like 'view:_id1:_id2:_id41:_id43:include1:shipper'

Today I was working on a form where the user can press a checkbox, and it will reveal additional entry fields.   I am using the features of Bootstrap to accomplish this.  It looks great and works like this:

When first loaded
With the checkbox checked
Using bootstrap, this is amazingly easy to accomplish.   The part that you want to initially hide is wrapped in a <div>  with a class="collapse in".  The collapse tells Bootstrap that this is the div you want to expand and collapse, and the "in" means that you want to start collapsed.

In the checkbox, you simply set two attributes:  data-toggle="collapse" and data-target="#the id of the div">.   That is all you have to do to make it work, really.

All went well until I changed from using a <div> to an <xp:div>.  I wanted more control over the attributes, specifically it was defaulting to "in" but I wanted to compute whether to start "out" or "in" based on whether the checkbox is already checked for existing documents.

When I started using the XSP div, I could not get it to open and close based on the checkbox.  It took me a long time of playing with the attributes using Inspect Element in Chrome to determine that Bootstrap does not like colons in ID's.

The fix is to escape your colons with a double backslash (\\) in SSJS.  When it gets to HTML, the value will be a single backslash which will escape the colon and in your id when referencing it.

This is NOT a good way of fixing this.  For a better way, see below.

I found out after I posted this that if the ID were to ever change then your code will break.   A big thanks to David Leedy who let me know that my code has potential issues.  He also pointed out to me that Marky Roden has made an excellent xsnipet for handling the unreadable colons.  

The Better Fix
  1. Copy Mark Roden's xsnipet into my clientside javascript library.
  2. Removed the attribute 'data-target' from your <xp:div>.
  3. In my $(document).ready() function, I added the following line of jQuery  $('input.checkbox').attr('data-target', x$("#{id:shipper}").selector);
UPDATE #2: I found out that if you do a partial refresh on the page, you will lose the fix if you leave it in the document.ready() as shown above.   The fix is to move the jQuery the clientside event of you partial update.

The jQuery was easy to write.  It simply adds an attribute to the checkbox calling the x$ function which selects the <xp:div> with a name of 'shipper'.   The '.selector' property returns the ID name.  This works just the same as before but is a much better way of coding this.  (Note: that I will only have one checkbox on this page, otherwise I would have to be more specific with my jQuery selector.)


I hope this saves someone some frustration when using ID's in Bootstrap.

Thursday, June 27, 2013

Dynamic Computed Values in an XPage without Javascript

Yesterday, I was trying to created a Computed Field on an Xpage that is based on the editable values of input fields on the same page.   My application is using managed beans to persist all backend data prior to writing it out to a NotesDocument.   Admittedly, I was a bit tired, but all I could think of how to do this was to use Serverside Javascript or Clientside Javascript.  I am comfortable with either method, but I had this nagging doubt that there must be a better way since I was already using java.

I decided to put in a Stack Overflow and ask what was the best practice for computed values using beans.   Stephan Wissel answered my question and the answer was fairly simple.   I was thinking along the correct lines that there was a better way than javascript.   Stephan said that I need to remove my set method from the bean, and to put my logic in the get method.

Sure enough, this worked just as he said.   I was somehow under the impression that each instance variable of the bean had to have one getter and one setter, like it was a required part of the bean specification.  It turns out that this isn't exactly correct, I commented out the set() method and it automatically turns the bound input to read only.  I never had learned this before so I thought it worthy of sharing.   Of course the nice side benefit of blogging this, is that I am now 10x more likely to remember this.

The logic for a computed field goes in the Get() method.  For some reason I was thinking the opposite.  To make the value dynamic, I put the computed value in a different container than the editable fields, and did a partial refresh of the container in the onChange event of each input field used in the calculation.  I really like the result.   All of this amounted to an additional six lines of java code in a method that I already had.

To create a dynamic field in summary:

  1. Bind directly to the bean using expression language.  If you are using beans, then you are likely already doing this.  I used an 'Edit Box' to store my 'computed field'.
  2. Comment out or remove the set----() method in the bean
  3. Put your business logic in the get----() method of the bean
  4. Put your dynamic field(s) in a different container element
  5. Refresh the container element using partial refresh

Friday, June 21, 2013

Simple Fix in Eclipse for when your Managed Bean Cannot Be Instantiated

Today, I added a second managed bean to my application.   I wrote the Java code and added the bean name, class, and persistence to my faces-config.xml file.

No matter what I did, as soon as tried to reference the bean, I would get an ClassNotFoundException.   Basically the application was not seeing the reference in the faces-config file.   I spent most of my energy thinking I somehow had improper syntax in the file.

A quick search found this Stack Overflow, and the comment by Mark Leusink did the trick.   I did a Project  | Clean in Eclipse, and the managed bean worked as expected.   The Project | Clean will launch a window where you can choose any or all of the projects in your 'Application' window.

Hopefully this will be helpful to someone.  I know I am going to try it whenever strange things are happening in my xpages projects.  BTW:  I am using Notes 9.