• UI Best Practices

last modified March 27, 2009 by ejucovy

the best practices for openplans ui design


Search Stuff

  • Alphabet Search Widget
Searching for all people whose name begins with a particular letter or all projects that begin with a particular letter, etc. can be well handled with the alphabet search widget found in this page.  The behavior is defined here.

  • Searching using Advanced Query
To do more advanced searches, beyond what portal_catalog can provide, use Products.AdvancedQuery as in this page.  You can do searches for strings ordered by relevancy in several fields with different relevancy weights and other cool stuff.  The info for AdvancedQuery is here.

  • List Pagination
For search results that span multiple pages and other multi-paged sets of data, use batch_macros.pt as in projects_searchresults.pt.  The resulting behavior is defined here.


String Interpolation

Use string interpolation over adding strings:
"%s/%s" %(name, name)
vs.
name + "/" + name

In TAL:
string:${obj/absolute_url}/{view/name}
is better than:
python: obj.absolute_url() + "/viewname"


Form pattern

1. define a form in a template.  do the following::

at the top::

<tal:handle-request
  replace="nothing"
  define="global fdata view/handle_request;">
 This could be used to return a variety of template friendly info
 regarding the processing of the form
</tal:handle-request>


for the form action, have it submit back to itself::

 <form name="create_form" method="post" class="enableUnloadProtection"
       tal:attributes="action string:${view/name}">

for the submit button, make it look something like this::

     <button type="submit"
         name="add:boolean"
         value="True"
         class="oc-button oc-chooseThis">
     create
     </button>

2. define a view class that looks like this::

from opencore.nui.base import BaseView, button

class SimplePostForm(BaseView):
   """an abstract base"""

   @property
   def name(self):
       return self.__name__

   @button('add')
   def handle_request(self):
       # do form submit stuff

3.  Bind your class to your template in zcml

How does it work?
----------------------

It's braindead as can be.  'button' merely causes handle_request to be skipped if 'add' is not in the request.  the template always calls handle_request. the view class defines handle_request.  Only one view is declared and all requests go through it. if we need multiple button dispatch, we probably should bite the formlib bullet for actions (ie ignore the form generation stuff except for the naming conventions). 


Forms should submit to themselves

octopus encourages forms to submit to themselves.  Perhaps you would like to read about how to use octopus .

It's a general principle(as well as the default action for a form) mainly for POST forms.  and generally, the pattern makes things simpler:

if formdata:
   validate data
   if errors:
       render form and errors
   else:
        do actions
        maybe redirect
else:
   render form

Normally, rendering the form will be the same template logic and submission would be required again on error.  Redirect only occurs when any business concerning the form is done.  The browser then can maintain state for form, back button works, etc.   REST principles dictate you have one location for actions effecting the state of a site and separate urls for viewing results.

The search forms are a bit different, since the initial search takes you to a searching page.  Eventually, this would be an internalized version of the same pattern as above, but using ajax to render and submit.  Currently, it bounces you away(which is ok but not great).   GET requests are better for this sort of stuff, because they can be bookmarked (ala results)