One of my sessions for BLUG, The Eureka Moment: The JSF Knowledge You Need To Understand XPages, has led me to investigate XPages optimisation and partial refresh processing to an extent I haven’t in the past. Much of the evidence gained has been illuminating for me too and has given me an understanding which will make me and my team think more when developing our XPages applications in the future. The JSF lifecycle and its quirks, seems to be a topic more developers have been delving into recently or hitting the impacts of:
- Tony McGuckin has posted an XSnippet for tracking the XPages and JSF lifecycle.
- Ulrich Krause has posted a topic on the XPages Forum about dataContext recalculating when outside of the refreshId area
- Andrew Pollack posted a question on Stack Overflow because he had suffered major frustration when trying to edit a row in a repeat control, because validation on a new document was triggering.
As part of my preparation for BLUG I investigated all the partial refresh options. One that I have never used but gave me a eureka moment, was Partial Execution Mode. It is the option that answered both questions, as I’ve been able to prove, thanks to both developers posting their code. (And, by the way, this is one of the wonderful benefits of XPages, that developers can post their code.)
So What Happens During a Partial Refresh?
Well, I can’t cover everything here. That’s a big chunk of my session at BLUG. And I’ll work from Ulrich’s code. There’s a screenshot below, but if you follow the link to his post above, you can download it.
First of all, the whole XPage is posted back to the browser. This is the default action and will occur for most of you for most of your XPages, unless specifically coded differently. That’s outside the scope of this post and won’t affect the later processing.
Secondly, the server processing occurs. I’ll come back to that.
Finally, the area in the refreshId, highlighted in the screenshot, is passed back to the browser.
RefreshId: WHICH Refresh?
We’ve mentioned refreshId here. The key thing to remember here is that the refreshId defines the area that will be refreshed back to the browser. The refreshId has absolutely no effect on the server processing, and that is very important to remember. Regardless of what is put in refreshId, the whole XPage is recalculated by the server processing, during the relevant phases of the JSF lifecycle. This is evidenced when you use a phaseListener to show what is updated during each phase (see Tony’s XSnippet for how to do that).
The value property of the dataTable is calculated during the Apply Request Values phase as well as the Render Response phase (see below).
Add a Computed Field with a value calculated in SSJS and you will see it is calculated during the same two phases. Add a rendered property calculated using SSJS to the Computed Field and you will see that the rendered property is recalculated during the Apply Request Values, Process Validation, Update Model Values and Render Response phases.
And by default, every control in the whole XPage is recalculated. The fact that this hasn’t been so widely noticed is a credit to the performance of XPages.
ExecMode and ExecId To The Rescue
On every event there is a little checkbox which a lot of developers – myself included – have always left as one of those pieces of advanced XPages functionality to look into at one point in the future. I reached that point recently.
The setting is Set partial execution mode. In the source pane this converts to execMode=”partial” set on the eventHandler. This changes what is processed in the server side processing. Instead of processing the whole XPage, it processes a specific component.
By default the component processed is not the component set in the refreshId property, but the component surrounding the eventHandler. In this case, that is the button. Herein lies the danger, because in the vast majority of cases, that won’t be what you want to process. That is the importance of setting the execId property as well. Note that the execId property is not available via the Events panel. You need to select the eventHandler and find it in the All Properties panel (the same as the onStart, onError and onComplete events use to trigger Client-Side JavaScript during the partial refresh). Personally, I find it easier to just set them in the source pane.
The Results
Once execMode is set to partial and execId is set, the full JSF lifecycle only processes those components within the ID defined for execId. This will significantly cut down the number of calls to the server. The value property of Computed Field controls will not be calculated at all during the JSF lifecycle. Currently the rendered property of the Computed Field and the value property of the Data Table will still get calculated during the Render Response phase. The screenshot below shows the value property of the Data Table. But the properties are not recalculated during any other phase.
It also means that if, like Andrew Pollack, you have a repeat control where you want to toggle edit mode and validation for one row while not triggering validation for a second datasource on the XPage, setting execMode and execId will do that for you.
The Caveat
However, bear in mind the impact of execMode and execId. Because the component tree is only updated for the components within the execId, that means other values entered by the user do not get applied during the Apply Request Values phase. That means all values entered by the user outside of the execId area revert back to what they were the last time a refresh occurred.
The Conclusion
So it has real benefits for performance, for logical processing, but the impacts must be understood. Hopefully this article has given you enough information to understand the impacts and, if not, enough information to work them out for yourself. It’s a piece of functionality I will certainly be using more often in the future.
During a partial refresh the values of data context variables, repeat controls etc. are recalculated every time, even the recalculation is not required. By using execMode / execId you can prevent that the recalculation is made multiple times, but not the recalculation itself. I have added a posting to my blog (on German) where you can see what I mean: http://hasselba.ch/blog/?p=572
There you will find a code snippet to prevent the recalculation if a component is not refreshed.
A better version which includes preventing of multiple execution of SSJS code can be found here:
http://atnotes.de/index.php/topic,53754.msg346960.html#msg346960
I will add this to the XSnippets asap.
Great post Paul.
I have explained this concept to others before but it is difficult to understand and this post will really help people.
It is a very cool feature. Like you I left it “until later” to figure it out. It is probably underused because it is not needed that often but when you need it, you really need it as Andrew learned.
Great article, thanks for sharing.
If you are using the extension lib, you can use
instead of data context. As far as I understood, it does pretty much the same as dataContext, but caches the calculated value. So during the pages first load,the return value for showRow is only calculated once and returned frommemory during a partial refresh.
ah sorry, the code snippet obviously does not render here …
I tried this in my application but it is not working. I am using dialog box of extension library(Domino 8.5.3 server).
I’m not sure what you’re trying and where. Partial refresh when closing the Extension Library dialog is done differently. There’s an extra parameter on the closing method that defines the ID of the element to refresh after closing, as documented in the book.
Paul,
I use Partial Update and love it! But is there a way to partial update multiple ids with SSJS?
I have several input text fields with onChange events that do a partial updates on panels (a panel for each field) that contain the images that display whether the required field is completed. But I also have a submit button at the top of the xPage, that only becomes usable when all required fields are completed. So, I’d like to be able to refresh that button also, from each required field (rather than a full update). That means refreshing two different objects in different places on the xPage (hence they can’t be in a single panel).
Thanks for any advice!
You can chain partial refreshes http://xpageswiki.com/web/youatnotes/wiki-xpages.nsf/dx/Work_with_events_and_partial_or_full_refresh#Run+multiple+partial+updates
Another option is to display the button based on whether or not there are error messages. I looked at this some time ago, but it doesn’t look like I blogged about it. I’ll dig out the example and blog it.
Ok, I’ll take a look at the chained partial refreshes again. I tried this once and it didn’t work, but I’ll try it again.
Another question for you, Paul – how do you disable the “back” function in the browser??? I would LOVE to be able to do that on some of my xpages!
Oh, and I am disabling the button based on whether or not the required fields are completed. But without a refresh, the button may remain disabled until the document is saved. I’m already doing a partial update on the panel that displays the icons for each field, letting the user know whether they’ve completed the required field or not, but I’d like to do a second update on the button.
Hi I used partial refresh. It works sometime. It gives error other time. It looses the connection from server. How can we avoid it. Thanks
One possibility is the session is timing out. If that’s the case, the Keep Session Alive control from the Extension Library is designed to prevent that occurring. If not, I’d advise posting on StackOverflow with more details of when or how it occurs.
Pingback: Using 2 lines of code to solve 2 days of frustration | Do Not Replicate
I found this post because we faced issues with partial execution on Domino 10. When “use a different ID for partial execution than for partial refresh” was a suggestion in Domino prior to v10 then this is now mandatory. Otherwise the server will throw an exception like this one:
HTTP JVM: CLFAD0380E: No component found with ID $$xspsubmitid=view:_id8:_id560:_id561:repeatRendered:0:_id564:_id565:_id570_0:_id580:_id596 to handle the submit event.. For more detailed information, please consult error-log-0.xml