XPages: Optimized partial refreshes for event handlers

September 20th, 2014

Sven Hasselbach has created an optimized version of the XSP.partialRefreshPost() method. This works great to reduce the number of form fields that are included in the request for a partialRefreshPost.

But his version does not include support for running server-side actions because the the form fields included in the POST doesn't include the server-side event handler.

So I have created an optimized version of his optimized version that supports this 🙂
My versions adds a 'submitid' parameter that should point to the id of the event handler that you want to execute on the server.

XSP.partialRefreshPost(
    '#{id:somePart}',
    {
      clearForm: true,
      submitid: '#{id:submitEventHandler}'
    }
);

Here's an example of using the optimized partial refresh for an eventhandler:

<xp:button id="button1">
    <xp:eventHandler event="onclick" submit="false" id="submitEventHandler" refreshMode="partial" refreshId="somePart">
        <xp:this.action><![CDATA[#{javascript:someServerSideAction();}]]></xp:this.action>
        <xp:this.script><![CDATA[
            XSP.partialRefreshPost(
                '#{id:somePart}',
                {
                    clearForm: true,
                    submitid: '#{id:submitEventHandler}'
                }
            );
        ]]></xp:this.script>
    </xp:eventHandler>
</xp:button>

Here's the complete code snippet (available as an OpenNTF XSnippets too):

	<xp:scriptBlock id="scriptBlockPROptimized">
        <xp:this.value><![CDATA[
			XSP.addOnLoad(function(){

			    // hijack the existing partial refresh method
			    if( !XSP.__partialRefresh ){
			        XSP.__partialRefresh = XSP._partialRefresh;
			    }

			    // add the new one to the XSP object
			    XSP._partialRefresh = function x_prfh(method, form, refreshId, options){

			        // clear the form?
			        if( options.clearForm ){

			            // create a new HTML form...
			            var newForm = document.createElement( "form" );
			            newForm.setAttribute( "method", form.method );
			            newForm.setAttribute( "action", form.action );

			            // ... and loop all existing fields
			            for( var i = 0; i<form.length; i++ ){
			                var field = form[i];
			                var fieldName = field.name;
			                var includeField = false;

			                try{

			                    // check for addition fields
			                    if( options.additionalFields ){
			                        includeField = dojo.indexOf(options.additionalFields, fieldName)!=(-1)?true:false;
			                    }

			                    // only add XPages relevant fields and addtional fields
			                    if( fieldName == form.id || fieldName.substr(0,2) == '$$' || includeField ){

			                        var newField = null;
			                        if( field.options ){
			                            // special handling for fields with options
			                            for( var j=0; j<field.length; j++ ){
			                                if( field.options[j].selected ){
			                                    newField = document.createElement( "input" );
			                                    newField.setAttribute( "type", "hidden" );
			                                    newField.setAttribute( "name", fieldName );
			                                    newField.setAttribute( "value", field.options[j].value );
			                                    newForm.appendChild( newField );
			                                }
			                            }
			                        }else{
			                            // default field handling: just clone the DOM element

			                            // check for $$xspsubmitid option
							            if( options.submitid && fieldName == "$$xspsubmitid"){
							                newField = document.createElement( "input" );
							                newField.setAttribute( "type", "hidden" );
							                newField.setAttribute( "name", "$$xspsubmitid" );
							                newField.setAttribute( "value", options.submitid );
							                newForm.appendChild( newField );
							            } else {
				                            newField = field.cloneNode( true );
				                            newForm.appendChild( newField );
				                        }
			                        }
			                    }
			                }catch(e){
			                    console.log(e);
			                }
			            }

			            // call the original refresh method with the new form
			            return XSP.__partialRefresh(method, newForm, refreshId, options);
			        }

			        XSP.__partialRefresh(method, form, refreshId, options);
			    };
			});
		]]></xp:this.value>
    </xp:scriptBlock>

Keep in mind that to optimize a partial refresh you need to focus on more than just the size of the POST request. You should also look at partial execution mode (execMode="partial") in order to reduce the amount of work that the server has to do.

Tags:

3 Responses to “XPages: Optimized partial refreshes for event handlers”

  1. Sven Hasselbach Says:

    NIce one!

    But I have to note that limiting the list of form fields in the POSTed data has a similar effect as using partial execution mode: Only the components are processed on serverside which are in the list (Converter, Validatiors, Data updates etc).
    As described here: http://hasselba.ch/blog/?p=1389

  2. Paul Withers Says:

    Good clarification, Sven. Basically, anything in the refresh area loses any changes in the browser. But the advantage of clearForm, presumably, is minimising the HTML passed to the server. I'd love to see a property on an eventHandler that allowed you to change it to a GET instead of a POST.

  3. Per Henrik Lausten Says:

    Of course Sven and Paul: If you don't pass on fields (so only use clearForm and not the additionalFields option) then they are not sent to the server and therefore not processsed (and also lost once the refresh area is refreshed).

    You need to use the additionalFields option to pass on necessary fields.