XPage Name Picker / Type Ahead for Edit Box Field

Mindwatering Incorporated

Author: Tripp W Black

Created: 12/21/2010 at 12:22 PM

 

Category:
Notes Developer Tips
JavaScript, XPages

Issue:
There is no names field yet and there still is not automatic way to pick a name.
So you still have to do work just like in a "classic" web page form.

Solution 1:
For a small address book where @Dblookup 64k limit won't be a problem, the solution is pretty simple:
1. Create an Edit Box field.
2. Under Properties tab, select the Type Ahead sub tab on the left.
3. Enable Type Ahead by checking its box.
4. Beside the suggestions field, choose the computed option, enter the following JavaScript (Server Side) code:
var dbname = new Array(@Subset(@DbName(), 1), "names.nsf");
return @DbColumn(dbname, "($Users)", 1);

For a large address box, you have two options:

Solution 2a:
One is a pop-up form, with the $VIMPeople view, a user selects the person and then you populate the field with the name selected from the embedded View Panel.
(you would change the links so that you don't open the doc, but populate the canonical name back to a local field on the pop-up for temporary storage or populate the source field directly via standard JavaScript client-side code.)
This option is longer than I have right now. It follows the same "classic" way of doing it but just in an XPage.

Solution 2b:
Still use type-ahead, but instead do a full script search as the user types and return just names matching it. The array returned can be more than 64k.
1. Create an Edit Box field.
2. Under the Type Ahead tab, enabled (check) type ahead.
3. At this point, a new child object for the Edit Box appears. In the Outline window, open the twistie and select Ajax Type ahead: typeAhead1.
4. Under All Properties tab, select the All Properties tab on the left.
Sample 1:
Type Ahead:
- ignoreCase = true
- minChars = 3 // since this returns a collection of ALL that start with this character, this is important!
- mode = partial
Data:
- valueMarkup = true
- var - searchValue
- valueList: <add the following code>
 var directory:NotesDatabase = session.getDatabase("", "test/spnames.nsf");
    var allUsers:NotesView = directory.getView("($Users)");
    var matches = {};
    var includeForm = {
        Person: true,
        Group: true
    }
    var matchingEntries:NotesViewEntryCollection = allUsers.getAllEntriesByKey(searchValue, false);
    var entry:NotesViewEntry = matchingEntries.getFirstEntry();
    var resultCount:int = 0;
    while (entry != null) {
        var matchDoc:NotesDocument = entry.getDocument();
        var matchType:string = matchDoc.getItemValueString("Form");
        if (includeForm[matchType]) { // ignore if not person or group
            var fullName:string = matchDoc.getItemValue("FullName").elementAt(0);
            if (!(matches[fullName])) { // skip if already stored
                resultCount++;
                var matchName:NotesName = session.createName(fullName);
                matches[fullName] = {
                    cn: matchName.getCommon(),
                    photo: matchDoc.getItemValueString("photoUrl"),
                    job: matchDoc.getItemValueString("jobTitle"),
                    email: matchDoc.getItemValueString("internetAddress")
                };
            }            
        }
        if (resultCount > 9) {
            entry = null; // limit the results to first 10 found
        } else {
            entry = matchingEntries.getNextEntry(entry);
        }
    }
    var returnList = "<ul>";
    for (var matchName in matches) {
        var match = matches[matchName];
        var matchDetails:string =
            "<li><table><tr><td><img class=\"avatar\" src=\"",
            match.photo,
            "\"/></td><td valign=\"top\"><p><strong>",
            match.cn,
            "</strong></p><p><span class=\"informal\">",
            match.job,
            "<br/>",
            match.email,
            "</span></p></td></tr></table></li>"
        ].join("");
        returnList += matchDetails;
    }
    returnList += "</ul>";
    return returnList;


- Sample 2:
5. Set the following attributes and code...
Type Ahead:
- ignoreCase = true
- minChars = 3 // since this returns a collection of ALL that start with this character, this is important!
- mode = partial
Data:
- valueMarkup = true
- var - tmplup
- valueList: <add the following code>

var dbsvr =@Subset(@DbName(), 1);
var searchView:NotesView = session.getDatabase(dbsvr ,"names.nsf").getView("($VIMPeople)");

// Creating a Lotus Notes search query. Notice the reference to lupkey!
var query = "(FIELD LastName CONTAINS *" + tmplup +"* OR FIELD FirstName CONTAINS *" + tmplup +"*)";

// Creating an array to store hits in
var searchOutput:Array = ["å","åå"];

// Doing the actual search
var hits = searchView.FTSearch(query);

var entries = searchView.getAllEntries();
var entry = entries.getFirstEntry();

// Build the array
for (i=0; i<hits; i++) {
searchOutput.push(entry.getColumnValues()[0]);
entry = entries.getNextEntry();
}
// sort the array
searchOutput.sort();

// clean to subset
var limitedSearchOutput = searchOutput.slice(0,Math.min(hits,20));
limitedSearchOutput = limitedSearchOutput.join( "</li><li>" ).replace( new RegExp( tmplup , "gi" ),"<b>" + $& "</b>");

return "<ul><li><span class='informal'>Suggestions:</span></li><li>" + limitedSearchOutput + "</li></ul>";
// Build the resulting output HTML code
var result = "<ul><li><span class='informal'>Suggestions:</span></li>";

var limit = Math.min(hits,20);
for (j=0; j<limit; j++) {
var name = searchOutput[j].toString();
var start = name.indexOfIgnoreCase(lupkey)
var stop = start + lupkey.length;
//Make the matching part of the name bold
name = name.insert("</b>",stop).insert("<b>",start);
result += "<li>" + name + "</li>";
}

result += "</ul>";
return result;


Notes:
2b: Credits for 2b method go to a Planet Lotus blogger Rasmus.
2b: Original code line:
limitedSearchOutput = limitedSearchOutput.join("</li><li>").replace(eval("/" + @LowerCase(tmplup )+"/g"),"<b>" + @LowerCase(tmplup )+"</b>").replace(eval("/" + @ProperCase(tmplup ) + "/g"),"<b>"+ @ProperCase(tmplup ) + "</b>");

Another solution from a blogger named Tim:
var directoryTypeahead = function (searchValue:string) {
// update the following line to point to your real directory
var directory:NotesDatabase = session.getDatabase("", "test/spnames.nsf");
var allUsers:NotesView = directory.getView("($Users)");
var matches = {};
var includeForm = {
Person: true,
Group: true
}
var matchingEntries:NotesViewEntryCollection = allUsers.getAllEntriesByKey(searchValue, false);
var entry:NotesViewEntry = matchingEntries.getFirstEntry();
var resultCount:int = 0;
while (entry != null) {
var matchDoc:NotesDocument = entry.getDocument();
var matchType:string = matchDoc.getItemValueString("Form");
if (includeForm[matchType]) { // ignore if not person or group
var fullName:string = matchDoc.getItemValue("FullName").elementAt(0);
if (!(matches[fullName])) { // skip if already stored
resultCount++;
var matchName:NotesName = session.createName(fullName);
matches[fullName] = {
cn: matchName.getCommon(),
photo: matchDoc.getItemValueString("photoUrl"),
job: matchDoc.getItemValueString("jobTitle"),
email: matchDoc.getItemValueString("internetAddress")
};
}
}
if (resultCount > 9) {
entry = null; // limit the results to first 10 found
} else {
entry = matchingEntries.getNextEntry(entry);
}
}
var returnList = "<ul>";
for (var matchName in matches) {
var match = matches[matchName];
var matchDetails:string =
"<li><table><tr><td><img class=\"avatar\" src=\"",
match.photo,
"\"/></td><td valign=\"top\"><p><strong>",
match.cn,
"</strong></p><p><span class=\"informal\">",
match.job,
"<br/>",
match.email,
"</span></p></td></tr></table></li>"
].join("");
returnList += matchDetails;
}
returnList += "</ul>";
return returnList;
}


Note:
photoURL - Used with Web and Mobile clients
ImagePath - Used with Notes embedded and Connect client.

previous page