Section 16.6. Creating Custom Widgets


16.6. Creating Custom Widgets

We've already seen how the creators of the WhatWhat Status project used custom widgets to display and dynamically create editable forms for Project data. You may want to go back and take another look at Chapter 7, "Controllers, Views, and JavaScript in the WhatWhat Status," to go over those widgets in light of your new insight into how widgets work.

Rather than go over that same ground again in this chapter, we're going to look at how two very different widgets work.

First, let's take a look at how TableForm works to render errors, and form fields on the page, and how we could customize the TableForm widget.

Here's the original TableForm widget code.

class TableForm(Form):     template = """     <form xmlns:py="http://purl.org/kid/ns#"         name="${name}"         action="${action}"         method="${method}"                  py:attrs="form_attrs"     >         <div py:for="field in hidden_fields"             py:replace="field.display(value_for(field), **params_for(field))"         />         <table border="0" cellspacing="0" cellpadding="2" py:attrs="table_attrs">           <tr py:for="i, field in enumerate(fields)"                          >               <th>                   <label                           for="${field.field_id}"                          py:content="field.label" />               </th>               <td>                   <span               py:replace="field.display(value_for(field), **params_for(field))"                   />                   <span py:if="error_for(field)"                                                  py:content="error_for(field)" />                   <span py:if="field.help_text"                                                  py:content="field.help_text" />               </td>           </tr>           <tr>               <td>&#160;</td>               <td py:content="submit.display(submit_text)" />           </tr>       </table> </form> """ params = ["table_attrs"] params_doc = {'table_attrs' : 'Extra (X)HTML attributes for the Table tag'} table_attrs = {}


As you can see, the bulk of the widget is the template code. You can either define your templates in a separate .kid file, and point to their location, or you can include them as a string right in the widget code itself as seen above.

You'll notice the error_for method is displayed just to the right of the field with an error. Some people prefer to see the errors formatted differently, or displayed above or before the form element. Fortunately, this kind of adjustment isn't hard at all. All you need to do is replace the widget's template with something more to your liking.

There are two ways we could go about doing this. We could create a CustomTableForm class very much like the TableForm, or we could just override the template attribute of the TableForm field whenever we instantiate a new TableForm object.

If the form is only being used once, it's probably easier to just override the template attribute at instantiation time. So, if we wanted to update the Bookmarker application that we built in Part 2, to use a custom form, we could change the bookmark_form instantiation to look like this:

bookmark_form = widgets.TableForm(template = "bookmarker.templates.custom_form",                                   fields=bookmark_fields(),                                   submit_text="Save Bookmark")


If, however, we wanted to use this new style form layout in several different places, or with several different forms, we can reduce code duplication by subclassing the TableWidget form like this:

class MyForm(widgets.TableWidget)     template  = "bookmarker.templates.custom_form"


Of course, we are skipping something. We need to create the custom_form.kid file to be used in either case. This is a standard Kid template file.

<form xmlns:py="http://purl.org/kid/ns#"     name="${name}"     action="${action}"     method="${method}"          py:attrs="form_attrs" >     <div py:for="field in hidden_fields"         py:replace="field.display(value_for(field), **params_for(field))"     />         <table border="0" cellspacing="0" cellpadding="2" py:attrs="table_attrs">         <tr py:for="i, field in enumerate(fields)"                      >             <th>                 <label                         for="${field.field_id}"                        py:content="field.label" />              </th>              <td>                  <span py:if="error_for(field)"                         py:content="error_for(field)">                     <br />                 </span>                  <span                   py:replace="field.display(value_for(field), **params_for(field))"                  />                  <span py:if="field.help_text"                         py:content="field.help_text" />              </td>         </tr>         <tr>            <td>&#160;</td>            <td py:content="submit.display(submit_text)" />         </tr>    </table> </form>


The only difference between this and the standard TableForm widget is that we moved error reporting above the value_for insertion and included a break after the error message display. Because the template checks the existence of the error with error_for(field), these will only be included in the table when there is an error.

Once you get the hang of it, widgets are infinitely customizable, and provide an excellent way to package up repeated code bits in highly customizable and reusable packages.




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