Showing posts with label XPages. Show all posts
Showing posts with label XPages. Show all posts

Friday, March 15, 2013

XPages: Returning to the Prior Page

I read this post from Brad Balassaitis and shared that "I should have blogged that last year when I did it".  A couple of people encouraged me to blog it just for a different perspective, so here we go.

I have a button in my left navigator that opens an XPage for processing documents with a bar code scanner. That XPage does not include the navigator; it's like a new document. Once the user is done processing, they click a Finished button and I wanted them to go back where they came from.

My method of saving the page is the same as Brad's: a sessionScope variable.  This is the code in an Execute Script action in the button before it opens the new page:
var curURL = context.getUrl().toString();
sessionScope.put("bundleProcReturn",@RightBack(curURL,"/"));
As I look at it, perhaps view.getPageName() would be more efficient, but I didn't know about it at the time and I can't argue with success. :)

In the Finished button, the Open Page action computes the Page Name value as:
sessionScope.get("bundleProcReturn");
It's really handy that so many things in XPages can be computed.  Once you understand that, you can use that feature to have your applications do exactly what you want.  It takes a little more time to keep track of things like the page you were on when you launched an action, but the user experience can be so much better - and easier for users transitioning from a Notes client app to the XPages version of the app.

That isn't to say I want to make my XPages apps look and behave like a Notes client app, but there are times when those behaviors are "expected" or "normal".

Saturday, February 2, 2013

XPages: Detecting and Logging Field Value Changes

Recently I had a request to add functionality to an XPages application I wrote and implemented last year.  They wanted the ability to log changes made in specific fields.  I did some searching and found the resources I needed:

valueChangeListener code as a managed bean by Tony McGuckin, which was linked from
valueChangeListener implementation code from Declan Sciolla-Lynch, which pointed to
A better way to update log fields by Chris Toohey

I used my "R&D skills" to set up the code in my application and was excited when it seemed to work right off the bat.  I moved it to the QA environment and then things started going bad.  I kept getting Null Pointer Exception errors when I tried to add and update documents.  I eventually realized a) I hadn't tried to create documents on the development server, and b) I hadn't tried to update documents that had never been updated previously.  Neither the valueChangeListener bean nor the addToList function were working if the starting value of the field was an empty string.

I still need to figure out how to identify an empty string in those fields to properly handle that situation, but I got around it by making a few changes in the code and by adding a default value to a field so it doesn't start as blank.

The "do something useful" code to put in Tony's bean ('c' is a UIComponent object):
// Get the name of the field that changed
String changedExpression = c.getValueBinding("value").getExpressionString();
int firstDot = changedExpression.indexOf(".") + 1;
int closingBracket = changedExpression.indexOf("}");
String changedField = changedExpression.substring(firstDot,closingBracket);
      
// Get the old and new values in the field
String oldValue;
try {
    oldValue = valueChangeEvent.getOldValue().toString();
} catch (NullPointerException npex) {
    oldValue = "";
} catch (Exception ex) {
    System.out.println("Major failure");
    ex.printStackTrace();
    oldValue = "";
};
String newValue = valueChangeEvent.getNewValue().toString();
      
Map viewScope = ExtLibUtil.getViewScope();
HashMap oldValuesMap = new HashMap();
HashMap newValuesMap = new HashMap();
      
if (viewScope.containsKey("oldValues")){
    oldValuesMap = (HashMap) viewScope.get("oldValues");
    newValuesMap = (HashMap) viewScope.get("newValues");
}
     
oldValuesMap.put(changedField, oldValue);
newValuesMap.put(changedField, newValue);
      
viewScope.put("oldValues", oldValuesMap);
viewScope.put("newValues", newValuesMap);
Code to read the old and new values and write them to a log field named ActivityHistory:
var dNow = @Now();
if (viewScope.containsKey("oldValues")){
    var oldValues:java.util.HashMap = viewScope.get("oldValues");
    var newValues:java.util.HashMap = viewScope.get("newValues");
    for (key in oldValues.keySet()) {
        actText = "Field " + key + " changed from '" + oldValues.get(key) + "' to '" + newValues.get(key) + "' - " + @Name("[CN]",session.getEffectiveUserName()) + " on " + @Text(dNow);;
        addToList(bundleDoc,"ActivityHistory",actText,"last");
    }
}
Updated addToList code (I used 'first' and 'last' instead of 'prepend' and 'append'):
function addToList(datadoc:NotesXspDocument, fieldname:string, newVal:string, addPos:string) {
    var newArr = new Array(datadoc.getItemValue(fieldname));
    switch (addPos) {
    case 'first':
        newArr.unshift(newVal);
        break
    case 'last':
        newArr.push(newVal);
        break
    }
    datadoc.replaceItemValue(fieldname,newArr);
    return true;
};

If anyone can explain what I missed with fields that start with a value of empty string, I'd appreciate it as I'm very early in my XPages and Java learning.  In the mean time, I hope this proves useful to others.