-
UI Best Practices
last modified September 26, 2007 by ejucovy
the best practices for openplans ui design
Search Stuff
- Alphabet Search Widget
- Searching using Advanced Query
- List Pagination
Static Content
Static text containing content that will occasionally be updated is stored inside of nui/static. The files there are named using the convention package_template_staticblock.txt. So main_projects_featured_projects.txt contains the featured text in the main/projects.pt "featured projects" section. It is included in the template with the command<div tal:content="structure python: view.render_static('main_projects_featured_projects.txt')" />
The better(and more easily testable) way to do this is to put the render call inside a method or a property on the view class ala:
class MyView(BaseView):
@property
def featured_projects_txt(self):
return self.render_static('main_projects_featured_projects.txt')
Then call in TAL like so::
<div tal:content="structure view/featured_projects_txt" />
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)