1
votes

Here is my inputText control with typeAhead enabled:

<xp:inputText id="inputNameEditBox">
    <xp:typeAhead 
        mode="full" 
        minChars="3"        
        ignoreCase="true"
        valueList="#{javascript:return mytypeAheadList();}"
        var="searchValue" 
        valueMarkup="true" 
        id="typeAhead1">
    </xp:typeAhead>
</xp:inputText>

SSJS mytypeAheadList() function calls custom Java userTools.userLookup() function to get a set of suggestions. (Our server cannot access corporate directory so we have to use LDAP HTTP Java API).

SSJS library:

function mytypeAheadList(){
    var v=new userTools.userLookup();  //Java library
    var usrList = v.getUserList(searchValue);
    var lenList = usrList.length;
    var retList = "<ul>";

    if(lenList>0){
        for (var i=0; i<lenList; i++) { 
            var matchDetails:string = ["<li>",@Name("[ABBREVIATE]", @Left(usrList[i], "@")),"</li>"].join(""); 
            retList += matchDetails;
        }       
    } else {
        retList += ["<li>","None found","</li>"].join("");
    }

    retList += "</ul>"; 
    return retList;     
}

So that means userTools Java object is created each time user type a character. Is there a way to avoid it, e.g. make var v a global variable on page load? Seems scope variables cannot accept Java objects.

2
Is your Java object serializable?Sven Hasselbach
No it's not serializableVladP

2 Answers

1
votes

I would do the following:

  1. Implement the Serializable interface to your POJO returned by getUserLookup. This allows to store the object in viewScope
  2. Limit the max size of lenlist. E.g. 20 results would reduce the time of looping, the size of the HTTP response and the performance in the browser
  3. Cache the result of the search (add searchValue and the resulting HTML string to a map). If a user hits backspace, the whole result must not be recomputed.
  4. Drop SSJS. Use Java.
  5. optional: If possible, precompute the results.

EDIT Something like this:

function mytypeAheadList(){

    // check if value is already cached
    if( viewScope.containsKey("search~" + searchValue) ){
        return viewScope.get("search~" + searchValue);
    }

    // get the userLookup object
    var v = null;   
    if( viewScope.containsKey("userLookup") ){
        v = viewScope.get("userLookup");
    }else{
        v = new userTools.userLookup();
        viewScope.put("userLookup", v);
    }

    // if usrList is "unlimited", limit the max size
    var usrList = v.getUserList(searchValue);
    var lenList = usrList.length > 20 ? 20 : usrList.length;

    // if getUserList has a restriction parameter
    var usrList = v.getUserList(searchValue, 20);
    var lenList = usrList.length;

    // build the list
    var retList = null;

    // reuse a variable is up to 2 times faster
    var matchDetails = null;

    if(lenList>0){
        retList = "<ul>";
        for (var i=0; i<lenList; i++) { 
            // concatenating a string is up to 2 times faster then join
            matchDetails = "<li>" + @Name("[ABBREVIATE]", @Left(usrList[i], "@")) + "</li>";
            retList += matchDetails;
        }
        retList += "</ul>"; 
    } else {
        // why join a static string?
        retList = "<ul><li>None found</li></ul>";
    }


    // put the result to the cache
    viewScope.get("search~" + searchValue, retList);

    return retList;     
}
0
votes

Yes you can do that.

Either you can put var v outside of your function to keep it loaded (and just lazy load it first time by checking if it is null).

Or you can put it all in a Java bean and let the scope of the bean determine how long you want to keep the data: ViewScope for just this page - ApplicationScope to allow all users to share it (and you can build in a check to force a maximum age of xx minutes - this could be relevant to consider if the data you look up could change).