It’s been a while since I last blogged because of a mixture of holidays and other extra-curricular projects. But last week I encountered a problem with a date converter having no effect when added to a Link control. I’m not sure if the converter was trying to convert the value property of the control, like it does for others. I suspect it probably is, because when I inspected the Java class created for the Custom Control, when I inspected the methods and hierarchy of the XspOutputLink class the getConverter() and setConverter() methods originating in the UIOutput class are not overridden in any of the classes lower in the hierarchy.
For those who got totally confused by this, believe me, a couple of months ago I would have been too. Add a link to an XPage or custom control, build it, open the Package Explorer view and under Local – xsp open the relevant XPage/Custom Control. Use Ctrl + F to find the reference to the ID you gave to the link, by default link1. You’ll see it creates a new XspOutputLink(). Highlight XspOutputLink and press F4. You’ll see the hierarchy of classes, and navigating up to UIOutput, you’ll see it has getConverter() and setConverter() methods. Navigating down through the classes you’ll see nothing else extends that getConverter() or setConverter() method. Incidentally, if you listen to the episode of The XCast on Themes, you’ll hear Tim Tripcony talking about the getStyleKitFamily() method, which you can use in SSJS to get the name of the control to use in themes. Look at the classes prefixed by UI and you’ll see that method.
So, anyway, as I said it looks like the Link’s converter will be running on the value property, not the text property. Personally, if that’s the case, I can’t envisage a use case where the link value would need converting but plenty where the link text would need converting. Perhaps it’s just one of those areas that slipped through the net, and hopefully it will be resolved. If my understanding of Java and what the code is doing is correct, it shouldn’t be too difficult to do so.
One workaround I considered was using a Computed Field control, which can accept a converter, and setting the tagName property to a. It’s not in the list, but it works. Unfortunately, it doesn’t style quite the same as a normal Link control. Tommy Valand kindly pointed me in the direction of a blog post on using the java.text.SimpleDateFormat class to either take a string and convert it to a date based on a specific format (e.g. dd/MM/yy) or take a date and convert it to a string of a specific format. So what the code is presenting to the browser is a string in a specific format, not a date which then needs formatting.
That was extremely useful, but I wanted to present the relevant short format depending on the user’s browser settings. But now I’m more confident with Java and have found a good documentation site (looking at most JavaDocs makes me understand why the 8.5.0 XPages documentation was not to the quality LotusScript developers have come to expect). So I decided to dig into the java.text.SimpleDateFormat class to see if I could find anything of use. SimpleDateFormat didn’t help, but java.text.DateFormat did. A bit of jiggery-pokery and trial and error and I had the following function that uses the DateFormat class to use the current user’s browser locale to get the date in the relevant format. The key function is getFormatter(), which takes a string. The mapping of what the string should be and the outputted format is in the comments. It uses context.getLocale() to get the current browser’s locale. I wasn’t sure if this would work, whether it would get the locale on the server, because it’s SSJS, but it does work . You’ll see from line 31 that the getDateInstance() method takes an integer. So why do I pass the format as a string? Well, I wanted to store it in an applicationScope variable, so an administrator can define the format for the application. Anyone who’s mixed Java and XPages knows that passing numbers to Java from XPages is a nightmare and equally in this scenario the value was stored as a double, which threw an error. So I store it as a string and run parseInt when I pass it across. Because the value will never be greater than 3 I’m not bothering with a radix for the parseInt function. If you’ve never come across it look at the difference between parseInt(9) and parseInt(9,10).
So now in a link, all I have to do is set the value as SSJS DateConverter.dateToString(doc.getCreated().toJavaDate(),applicationScope.dateStyleNumber) and I get the date in the standard application-wide format. Note that the function requires a Java date, but the toJavaDate() function easily handles that.
-
var DateConverter = {
-
dateToString: function( date:java.util.Date, fmt:int ){
-
try {
-
if( !date ){ return ”; }
-
var formatter = DateConverter.getFormatter( fmt );
-
return formatter.format( date );
-
} catch( e ){
-
// error handling
-
}
-
},
-
stringToDate: function( dateString:String, fmt:String ){
-
try {
-
if( !dateString ){ return null; }
-
var formatter = DateConverter.getFormatter( fmt );
-
return formatter.parse( dateString );
-
} catch( e ){
-
// error handling
-
}
-
},
-
getFormatter: function( fmt:String ){
-
try {
-
// 2ÞFAULT,FULL=0,LONG=1,MEDIUM=2,SHORT=3
-
var cacheKey = ‘dateFormatter’ + fmt;
-
var locale = context.getLocale();
-
var dateFormatter = applicationScope[ cacheKey];
-
if( !dateFormatter ){
-
dateFormatter = java.text.DateFormat.getDateInstance(parseInt(fmt),locale);
-
applicationScope[ cacheKey + “~” + locale ] = dateFormatter;
-
}
-
return dateFormatter;
-
} catch( e ){
-
// error handling
-
}
-
}
-
}