The DataView is an extremely powerful control that was added with the Extension Library. I was never a huge fan of the View Control. It was limited in its datasources and layouts. But the DataView accepts any datasource, provides a nice out-of-the-box layout as well as allowing flexibility with facets. Unsurprisingly it took up a large portion of Chapter 7 (which I wrote) in the XPages Extension Library book.
But a couple of times now I’ve come across errors when nesting repeats within the detail facet. In both scenarios I was looking to include content from related documents, so the source for the repeat was:
#{javascript:ent.getDocument().getResponses()}
.
That worked fine on page load, but sometimes during a partial refresh it threw an error with a NullPointerException on that piece of code. This is where understanding the JSF lifecycle and being able to read the relevant parts of a stack trace come in useful. The error was similar to one I’ve blogged about before with a rendered property. The key bit to look for in the stack trace is a line starting “at com.sun.faces.lifecycle.” which will then have the name of the relevant phase. In this case, as in my previous blog post, it was ApplyRequestValuesPhase.
But the value property of a repeat control doesn’t really need to be calculated in that phase unless the components in the repeat are editable. That’s because Apply Request Values phase writes data from the browser into the submittedValue property of components. It doesn’t need to be run in the Process Validations phase either, unless the components are editable. I’m not sure whether they need to be there earlier than the Render Response phase, but I suspect they do, because I know the value property of a repeat control cannot be set from a theme. So I used the Phase Listener code I blogged about yesterday to store the current phase in a requestScope variable. That allowed me to prevent the code from trying to get the backend document from the NotesXspViewEntry during the early phases, by changing the value property to the following computation:
#{javascript:(requestScope.get("currPhase") < 4) ? null : ent.getDocument().getResponses();}
That means it’s just returning null if the currPhase is before Update Model Values, and only then trying to get the document’s responses. At this point my focus was on preventing errors, which this did, rather than trying to optimise it to the nth degree. It may be that returning null before the Invoke Application phase works as well, I haven’t tested to that extent.