Modifying Spry's Auto Suggest Widget to Bind User's Selection to A Form
I recently had to develop a new administrative interface for our staff to use to track presenters and update their contact information. I decided to use Adobe's Spry ajax tool set to make the pages more interactive and to save time for staff. One use case I had was staff needed to be able to find a specific presenter using his last name (which is not unique among presenters) and then update his contact information.
I could have fulfilled this use case by having the staff person enter the last name, return a page with presenters matching that last name, then have the user select the specific presenter from all those with that last name, and then populate the update contact form. But I had recently been learning how to use Spry's auto suggest widget and decided to try to modify the widget so that the staff person could start typing the presenter's last name and Spry would show possible matches. Then the user could select the correct presenter and the form fields would be populated with that presenter's information. I wanted to do all these tasks without a page refresh.
You can view a demonstration of the solution here: /spry/autocompleteexample/index.htm
Here is where you can see an example provided by Adobe on how the Spry auto suggest widget works before I modified it: http://labs.adobe.com/technologies/spry/samples/SuggestSample.html. My task was to modify the associated SpryAutoSuggest.js functions so that when the user clicked on one of the suggestions, the form field values would be updated with that selection's values. Unmodified the auto suggest widget merely updates the text field's value.
Since my JavaScript programming skills are poor and there is not much documentation on the Spry widgets, it took some trial and error to find modifications that work. I recommend you backup the SpryAutoSuggest.js before following what I did.
My first step was to add a div containing my update form that was tied to the data set (which I called dsPresenters).
<div id="detailsDiv" spry:detailregion="dsPresenters" >
<p><b>Update Presenter's Information</b></p>
<form action="#" method="post" name="updateForm">
<table>
<tr>
<td>First Name: </td><td> <input type="text" name="firstName" value="{FIRSTNAME}" /></td>
</tr>
<tr>
<td>Last Name: </td><td> <input type="text" name="lastName" value="{LASTNAME}" /></td>
</tr>
<tr>
<td>Phone: </td><td> <input type="text" name="lastName" value="{PHONE}" /></td>
</tr>
<tr>
<td>EMAIL: </td><td> <input type="text" name="lastName" value="{EMAIL}" /></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" name="submit" value="Update" /></td>
</tr>
</table>
<input type="hidden" name="presenterid" value="{PRESENTERID}" />
</form>
</div>
<p>The above form fields will then be given the value of the corresponding field names for whatever row is currently selected in the data set. When the web page first loads, the currently selected row will be the first row in the data set.</p>
<p>The user can then type in the last name of a presenter. As the user types, Spry show rows from the data set whose last name field value matches what the user has typed so far. At any time the user can click on one of the suggestions.</p>
In the SpryAutoSuggest.js is an attribute setValue that is assigned a function (which coming from Java and CF was a new concept to me--assigning a function to a variable name--ugh). This function is executed when the user clicks on one of the suggestions. Unmodified this function merely sets the value of the text field to the value of the string passed to this function. I needed to modify this function so that data set would be filtered by the string and the current row of the data set would be set to the row that had the column that matched the string.
Below is the modified version of this function.
Spry.Widget.AutoSuggest.prototype.setValue = function(str)
{
if (!this.textElement)
return;
this.textElement.value = str;
var regExpStr = str;
var regExp = new RegExp(regExpStr, "i");
var filterFunc = function(ds, row, rowNumber)
{
var str = row[ "PRESENTERID" ];
if (str && str.search(regExp) != -1)
return row; /* MATCH! */
return null; /* NO MATCH! */
};
this.dataSet.setCurrentRowNumber( this.dataSet.getRowNumber( this.dataSet.filter(filterFunc) ) );
this.showSuggestions(false);
}
What I added was a filter function inside this function. The filter function will find the row that matches the string provided to this function. That string is no longer the last name but is the PRESENTERID value for the row that the user clicked on among all the suggestions. I have the data set set its current row number to the row number value for the row found by this filter function.
I also had to modify the function used to create an object of the SpryAutoSuggest widget. Below is the modified code.
Spry.Widget.AutoSuggest = function(textElement, suggestRegion, aDataSet, queryFunc)
{
this.textElement = $(textElement);
this.region = $(suggestRegion);
this.dataSet = aDataSet ;
I added a parameter (aDataSet) for the data set being used to populated the auto suggestions. This way I can modify that data set in the function assigned to setValue (see above).
Lastly, I had to change the code that creates a Spry AutoSuggest object on the page that the user views. Here is the modified code:
<InvalidTag>
var ac2 = new Spry.Widget.AutoSuggest("presenterIDTF", "memberMenu", dsPresenters, function(acWidget, str) { MyQueryFunc(acWidget, str, dsPresenters, "LASTNAME"); });
</script>
I pass to the AutoSuggest's constructor function the name of the text field the user types into, the id value of the dynamic region used to show the suggestions, the name of the data set, and a function that will be used to filter the data set as the user types into the text field. Note the field LASTNAME is being used to filter the data set for displaying the auto suggestions. But study the code where I modified the function assigned to setValue and you will see that to filter the data set so I can find the correct row to update the form fields, I'm using the PRESENTERID value of the row. This is necessary because last name is not unique, but presenter id is.
Spry pulls the string value sent to the setValue function (and which I use to compare to the PRESENTERID) from the column named as the value for spry:suggestion, as shown in the code below.
<div id="memberMenu" spry:region="dsPresenters" style="display: none;">
</p>
<table>
<tr spry:repeat="dsPresenters" spry:hover="hover" spry:suggestion="{PRESENTERID}" >
<td><div class="boxshot">{PRESENTERID} {LASTNAME}, {FIRSTNAME}</div><div>{EMAIL} {PHONE}</div></td>
</tr>
</table>
</div>
That is why when the user clicks on one of the auto suggestions, the text field is then updated with the PRESENTER ID value for the row the user selected.
You can view a demonstration of the solution here: /spry/autocompleteexample/index.htm
In my production code the solution works well and saves the staff person some time since they no longer have to load additional pages to find the specific presenter.
As I mentioned my JavaScript programming skills are poor, so if you know a better way to get Spry to do this, let me know. The key is that I want to allow the user to start typing in the presenter's last name.
One thing I'd like to do which I haven't figured out yet is to allow users to enter either the first or last name into the text input in order to see a list of suggested matches. Right now I'm stuck with first name and last name text input boxes updating the same set of results (although that still works pretty well). Let me know if you have any ideas.
http://labs.adobe.com/technologies/spry/samples/au...
(See: "Multiple dataset fields lookup")
Do you intend to update your modifications to the latest version? If so I would be very interested in knowing how you did it.
-ken-