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.

Tuesday, June 11, 2013

My First Experience with Managed Java Beans in an Xpages Application

In my latest application, I decided early on that I was going to use Managed Beans for my business logic.   Prior to starting I read and listened to everything I could find about them.   I am not going to explain how they work in this post, except to point you in the direction of the resources I used.

I learned about JavaBeans way back when I first started studying Java.   I learned Java by taking three credit classes at Northlake College in Irving, TX.  My instructor worked at IBM and was an excellent teacher.  JavaBeans were basically one chapter towards the end of my studies.  I think Java Server Faces, of which xpages is an implementation of, had just come out.   JSF was only touched on in lecture but was too new for the textbook.

Fast forward to today, I am pretty sure it was a Notes in 9 episode when I first heard about them.   I had already created two xpages applications without using them, so I really wanted to know what is in it for me.

The best resources today that I would recommend to learn to use managed beans is to watch Russ Maher's 2 hour Jumpstart from Connect 2013.   IBM has posted the video here (Thank You IBM!!).   It covers the same material he used in his Notes In 9 episodes in even greater detail.   Another great resource is this youtube presentation on Java for Domino Developers by Paul Calhoun.  It also runs two hours.

My use of Managed Bean is fairly simple at this point.   I use them to hold the data they enter as they click through a data entry wizard.   The data is only stored in the bean until they get to the final step.   I then write the data as a new document.   If the users quits in the middle, then I wipe clean the contents of the bean.  The bean also allows users to go forwards and backwards through the screens and see the data they entered.  Later, if the user edits the existing document, the bean is reloaded and then updates the backend document upon completion.   It can be done but it would be a pain to do all this without using managed beans.

Here is my code.   Like I said, this is pretty simple, but I think would be good to someone just getting started. I make a few comments in red.   I also cut out most of the instance variables to make this shorter.   The scope my bean is using is "session'.

import javax.faces.context.FacesContext; 
import lotus.domino.Database;
import lotus.domino.Document;
import java.io.Serializable;
@SuppressWarnings({"serial"}) 
 **I saw someone else do this to suppress warnings so I copied it,think it must be part of Eclipse**

public class StatusBean implements Serializable {
private String status;
private String typePO;
private String unid;

**These getter's and setters were generated by Eclipse. They are required**

public String getTypePO() {
return typePO;
}
public void setTypePO(String typePO) {
this.typePO = typePO;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public void setUnid(String unid) {
this.unid = unid;
}
public String getUnid() {
return unid;
}

public StatusBean(){
                //zero argument constructor 
**You can have other constructors if you want but you must have one like this.**
}

 **This method wipes the contents of the bean, used when cancelling***
public void deleteAllHeaders(){
this.typePO = null;
this.status = null;
this.unid = null;
}

***This method saves the new document and returns the document unique id to the caller***
public String saveNewDocument(){
String unid = "";
try{
*One very long statement below*
Database database = (Database)FacesContext.getCurrentInstance().getApplication()
.getVariableResolver().resolveVariable(FacesContext.getCurrentInstance(), "database");
Document doc = database.createDocument();
doc.replaceItemValue("form", "PO");
doc.replaceItemValue("status", status);
doc.replaceItemValue("typePO", typePO);
doc.save();
unid = doc.getUniversalID().toString();
}
catch(Exception e){
e.printStackTrace();
}
return unid;  //return new UNID to the calling SSJS function
}
}

Even if you don't know Java, I wouldn't call this hard stuff.   It really doesn't read much different than server-side JavaScript.   I haven't written the methods to edit and update existing documents yet, but I don't see that being very complicated.   I have to say I really love this way of coding.  

Wednesday, June 5, 2013

Datepickers in Bootstrap that are Contained in a Modal Window

The current version of Bootstrap amazingly does not contain a UI datepicker element.   There are ways around this of course.   Someone in Romania named Stefan Petre created a datepicker for bootstrap that is widely used.  Kudos to Stefan for filling that gap.

Mark Leusink of http://bootstrap4xpages.com has taken Stefan's Datapicker and created an xpages custom control.   This control is available here and the code for the custom control is here.   If you use this control, you will need to recreate the custom parameters by using this xsp-config file.   If you are not using the datepicker in a modal window, then this should work great for you.

Unfortunately for me, I tried for most of a day to get this working in my project but was unsuccessful.  The issue that I had was that the date window would not display correctly no matter what I tried.

Full calendar would not display
If you have read my last few posts, you will know that I am creating a responsive application in Bootstrap using a series of Modal Windows that act like a wizard.   These windows work nicely, even on an iPhone 4.   Being able to nicely add dates is essential in any screen size.   Initially I tried using the Dojo date text box control, and even the date time picker core control, but neither would work.   I then tried the Bootstrap datepicker after reading a recent blogpost by Karl-Henry Martinsson where he mentions it.

I could sort of get the Bootstap datepicker to work but had the following issues.
  • Clicking on the data icon would not work, but clicking in the field did work
  • The datepicker wouldn't work unless you clicked in the field, clicked out, and then back in
  • If you enter a value, then go back in again, the UI would mess up as shown in the screen print
  • The datepicker wouldn't close after selecting a value, you had to click outside the calendar to close it
Calendar messed up when re-editing date field
Here is how I went about fixing this mess.   
  1. I could never get the calendar icon button to click for entering the value.  I decided that I would just accept that you click in the field and get the calendar.   I think this works fine, even though I would have rather used a button.   The default is to highlight today's date which I like.
    The finished product
  2. To fix the 2nd and 3rd bullet point above I wrote the code as shown in the screen print.  This is because I discovered that $('.datepicker').datepicker('show'); caused it to open correctly the first time, but caused the UI to progressively mess up (see second screenprint) upon editing an existing value.   When I used $('.datepicker').datepicker(); then the UI acted normally, and since there was already a value in the field, it worked right away.  (I did not want a default value.)  This client side javascript code is in the onClick event of the <xp:inputText> tag. 
    This was the fix to get it to work
        3.   To get the calendar to close when selecting a value, I added a <xp:scriptBlock> containing:                    $('.datepicker').datepicker()
    .on('changeDate', function(ev){
    $('.datepicker').datepicker('hide');
     });

    The on('changeDate') method is part of the api.  There is a similar example on the Bootstrap Datepicker page.

      4.   Lastly, you will surely want to turn autocomplete="off".   For whatever reason, I was also unable to size the input field using the bootstrap value="input-small" .   Because of this, I set the width to using "em", which is a variable sizing measurement.   You wouldn't want to use pixels in this case.

    Note:  I didn't mention earlier, but you have to install the datepicker CSS and Javascript in your database first.   In you input field, you MUST set the style to "datepicker".

    Update:  To use more than one datepicker in a single modal window, wrap each in a <div> with a different id.   In the onClick event, reference the id in the jQuery selector.

    Conclusion:   This whole exercise was a big pain, but necessary.  If you are not using a Datepicker in a modal dialog then you are better off using Mark Leusink's custom control or just copy code from Stefan Petre's sight.  If you are using a modal dialog to display the calendar date then this will help you.   If you have any alternatives or questions, leave a comment.