Section 16.7. Anatomy of an Ajax Widget


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.

In many ways the AutoCompleteField is just like any other widget; it defines a template, and a couple of params. The template sets up a script tag and creates a new JavaScript Object called AutoCompleteManagerX, where X is replaced by the field_id. This keeps one AutoCompleteManager from stomping on another when you set up several AutoCompleteFields on the same page. The constructor for the JavaScript AutoComplete function takes several parameters, which are pulled from the widget's params, which we'll discuss after we take a closer look at the widget code itself.

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:

class AutoCompleteField(CompoundFormField):     """Performs Ajax-style autocompletion by requesting search     results from the server as the user types."""     template = """     <div xmlns:py="http://purl.org/kid/ns#">        <script language="JavaScript" type="text/JavaScript">          AutoCompleteManager${field_id} = new AutoCompleteManager('${field_id}',            '${text_field.field_id}',            '${hidden_field.field_id}',            '${search_controller}', '${search_param}',            '${result_name}',${str(only_suggest).lower()},            '${tg.url([tg.widgets, 'turbogears.widgets/spinner.gif'])}');          addLoadEvent(AutoCompleteManager${field_id}.initialize);     </script>     ${text_field.display(value_for(text_field), **params_for(text_field))}     <img name="autoCompleteSpinner${name}"       src="/books/4/370/1/html/2/${tg.url([tg.widgets, 'turbogears.widgets/spinnerstopped.png'])}"      alt="" />     <div  />     ${hidden_field.display(value_for(hidden_field), **params_for(hidden_field))}     </div>     """     javascript = [mochikit, JSLink(static,"autocompletefield.js")]     css = [CSSLink(static,"autocompletefield.css")]     member_widgets = ["text_field", "hidden_field"]     params = ["search_controller", "search_param",                      "result_name", "attrs", "only_suggest"]     text_field = TextField(name="text")     hidden_field = HiddenField(name="hidden")     attrs = {}     search_controller = ""     search_param = "searchString"     result_name = "textItems"     only_suggest = False


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)


As you can see it's not hard to wrap an existing JavaScript funtion into a reusable Widget. Writing autocomplete.js would take considerably more time. The interface for using AutoComplete widgets is flexible and easy to use. Widgets make this all possible, and it's not hard to create your own widgets, or pick up new widgets from the CogBin.




Rapid Web Applications with TurboGears(c) Using Python to Create Ajax-Powered Sites
Rapid Web Applications with TurboGears: Using Python to Create Ajax-Powered Sites
ISBN: 0132433885
EAN: 2147483647
Year: 2006
Pages: 202

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net