16.7. Anatomy of an Ajax Widget
Widgets are a part of the TurboGears view layer, so Ajax widgets need to work in cooperation with your application's controller methods to get data back from the server. The AutoCompleteField widget defines a couple of important params which are used to control not only how the widget works, but also which controller method should be called when the user starts typing.
There are two major parts of the auto-complete widget: the first is widget code presented here, and the second is the autocomplete.js file which contains all the Java-Script necessary to make everything work.
There's a separate div class setup for the autoCompleteResultsX (again X will be replaced by the field_id). The autoCompleteManager based function does all the real work calling the server, parsing the results, and populating the div which displays possible matches.
Here's the widget code itself:
The params in this widget are all pretty important. But the single most important is the search_controller which tells the widget which URL on the server ought to be called to get a list of possible matches.
The AutoCompleteManager based function will call the search_controller URL with a single key-value pairthe key is whatever you've used as search_param, and the value is the current contents of the field.
The controller should return a JSON object, containing a key that matches the value of the result_name param. That result_name key should map to a list of strings that contain all the possible matches.
The last important param is only_suggest, which governs whether the AutoComplete field will accept strings that don't match the results received from the server. The default is False, which means thatunless you set only_suggest to Truethe field will allow the user to enter a brand new value that does not match anything returned from the server.
All of this can seem a bit abstract, so lets's take a look at an example that uses the AutoComplete widget:
class DeleteUser(WidgetsList): username = AutoCompleteField(search_controller = "/search_username", search_param = "search", result_name = "usernames") task_form=TableForm(fields=DeleteUser())
Here's the search_username method:
@expose(allow_json=True) def search_username(self, search): matching_users = User.select(User.q.username.startswith(search)) usernames = [user.username for user in matching_users] return dict(usernames=usernames)