Quick Tip: A View-Filtering Search Box

Tue Sep 16 21:31:16 EDT 2014

Tags: xpages

One of the problems that crops up in some situations in XPages is the one described here: executing Ajax queries in too rapid a succession can cause the browser to cap them out until a full refresh. Depending on how you're encountering the problem, there may be a built-in solution: XSP controls that execute Ajax requests often have a throttling or latency parameter, and the same applies for "manual" JS widgets like Select2 (called "quietMillis" there).

Another such situation is the topic of this code snippet: a "filter view" control that allows the user to type and executes partial refreshes for each keypress or clearing of the field. To solve this, I found a block of code I wrote years ago, but should do the trick nicely. It uses setTimeout and clearTimeout to do this sort of delayed search and throttling. As I recall, it worked pretty well, though you'd want to increase the 500ms latency if the request usually takes longer, I suppose (or improve your page speed).

The code is from a Custom Control with styleClass, style, and refreshId properties and stores its value in viewScope.searchQuery.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
	<xp:div styleClass="lotusSearch #{compositeData.styleClass}" style="#{compositeData.style}" id="searchBox">
		<xp:inputText id="searchQuery" styleClass="lotusText" value="#{viewScope.searchQuery}" type="search">
			<xp:this.attrs><xp:attr name="placeholder" value="Search"/></xp:this.attrs>
			<xp:this.onkeypress><![CDATA[
				if(event.keyCode == 13) {
					if(window.__searchTimeout) { clearTimeout(window.__searchTimeout) }
					XSP.partialRefreshPost("#{javascript:getComponent(compositeData.refreshId).getClientId(facesContext)}", {})
					return false
				}
			]]></xp:this.onkeypress>
			<xp:eventHandler event="search" submit="false">
				<xp:this.script><![CDATA[
					// search is fired when the "X" in WebKit is clicked to clear the box
					if(window.__searchTimeout) { clearTimeout(window.__searchTimeout) }
					window.__searchTimeout = setTimeout(function() {
						XSP.partialRefreshPost("#{javascript:getComponent(compositeData.refreshId).getClientId(facesContext)}", {
							execMode: "partial",
							execId: "#{id:searchBox}"
						})
					}, 500)
				]]></xp:this.script>
			</xp:eventHandler>
			<xp:this.onkeyup><![CDATA[
				// Keypress doesn't fire for deletion
				if(event.keyCode != 13) {
					// Let's try some trickery to auto-search a bit after input
					if(window.__searchTimeout) { clearTimeout(window.__searchTimeout) }
					window.__searchTimeout = setTimeout(function() {
						XSP.partialRefreshPost("#{javascript:getComponent(compositeData.refreshId).getClientId(facesContext)}", {
							execMode: "partial",
							execId: "#{id:searchBox}"
						})
					}, 500)
				}
			]]></xp:this.onkeyup>
		</xp:inputText>
	</xp:div>
</xp:view>
New Comment