You are not logged in Log in Join
You are here: Home » Members » AlexR » Using selection lists

Log in
Name

Password

 

Using selection lists

Version 0.3

This howto show basic ways to implement a selection list (i.e. a drop-down list or multiline selection area) in a form. Most information were found on the [email protected] list and looking at the Zope UI pages. Feel free to send me your feedback.

Version History

  • Version 0.3 - 7th November 1999 - Added example for Python methods
  • Version 0.2 - 3rd November 1999 - Corrected layout error and added section about external methods
  • Version 0.1 - 31st October 1999 - First draft

Using the SELECT tag in forms

To create a drop-down list, use a SELECT tag within FORM tags. In SELECT tags, each option is itemized by an OPTION tag. These tags are not Zope-specific; they are standard HTML tags. For more information, see the HTML 4 Specification or an HTML tutorial.

Let's create a basic selection list with hard-coded options.

<FORM action="myMethod">
...
<SELECT name="myList">
 <OPTION value="optionName1">optionValue1</option>
 <OPTION value="optionName2">optionValue2</option>
 <OPTION value="optionName3">optionValue3</option>
 <OPTION value="optionName4">optionValue4</option>
</SELECT>
...
</FORM>

The displayed part is optionValueN. The SELECT structure need to be located in FORM tags. I will omit the FORM tags below for short.

Within the SELECT tag, you can specify attributes (these are standard HTML attributes):

  • multiple - allows multiple option to be selected
  • size=N - creates a selection area N rows high
  • disabled - control is unavailable in this context
  • tabindex - position in tabbing order

Example:

<SELECT name="myList" multiple size="4">
 <OPTION value="optionName1">optionValue1</OPTION>
 <OPTION value="optionName2">optionValue2</OPTION>
 <OPTION value="optionName3">optionValue3</OPTION>
 <OPTION value="optionName4">optionValue4</OPTION>
</SELECT>

Additionally, DTML allows to specify the type of the data returned by the list. To specify a type, append a colon (:) and a type name to the list name.

Example:

<SELECT name="myList:list" multiple size="4">
 <OPTION value="optionName1" selected>optionValue1</OPTION>
 <OPTION value="optionName2">optionValue2</OPTION>
 <OPTION value="optionName3" selected>optionValue3</OPTION>
 <OPTION value="optionName4">optionValue4</OPTION>
</SELECT>

Here the myList selection will be returned in list format. You can specify other types. For more information about these types in forms, see the Form Variable Types and Typechecking Howto.

Within the OPTION tag, you can also specify attributes (standard HTML attributes here too):

  • selected - allows to preselect one or several lines
  • disabled - control is unavailable in this context

Example:

<SELECT name="myList" multiple size="4">
 <OPTION value="optionName1" selected>optionValue1</OPTION>
 <OPTION value="optionName2">optionValue2</OPTION>
 <OPTION value="optionName3" selected>optionValue3</OPTION>
 <OPTION value="optionName4">optionValue4</OPTION>
</SELECT>

Here option 1 and 4 are preselected and displayed in a 4-line selection box.

Filling lists from loops

Using loops

You can fill a list from a loop. You use the dtml-in tag to specify the object to loop on. When looping on an object, you can directly access its properties within the dtml-in tags. For more information about this tag, see the Z Document Template Markup Language Guide. The following code creates a selection list of DTML document titles:

<SELECT name="myList">
 <dtml-in "objectValues(['DTML Document'])">
   <OPTION value='<dtml-var id>'><dtml-var title></OPTION>
 </dtml-in>
</SELECT>

For more information about loops, see the Looping in DTML Howto.

You can also loop on a list of hard-coded values. Example:

<SELECT name="myList">
 <dtml-in "('un', 'deux', 'trois', 'quatre',
           'cinq', 'six', 'sept', 'huit',
           'neuf', 'dix')">
   <OPTION value="<dtml-var sequence-item>"><dtml-var 
   sequence-item></OPTION>
 </dtml-in>
</SELECT>

You get a nice list with French figure names. How wonderful for you.

You can also specify a list of figures. Example:

<SELECT name="myList:int">
 <dtml-in "(0, 1, 2, 3, 4, 5 ,6, 7, 8, 9)">
  <OPTION value="<dtml-var sequence-item>"><dtml-var 
    sequence-item></OPTION>
 </dtml-in>
</SELECT>

Testing and excluding values

You can use Python expressions to test values. This allows you to preselect or filter out values from your list. Example:

<SELECT name="myList">
 <dtml-in "objectValues(['News'])">
  <dtml-if "id() == 'foo'">
   <OPTION SELECTED VALUE='<dtml-var id>'>
   <dtml-var title_or_id></OPTION>
  <dtml-else>
   <OPTION VALUE='<dtml-var id>'>
   <dtml-var title_or_id></OPTION>
  </dtml-if> 
</dtml-in>
</SELECT>

The following example exclude objects whose type is "News".

<SELECT name="myList">
 <dtml-in objectItems>
   <dtml-if "meta_type != 'News'">
     <OPTION><dtml-var id></OPTION>
   </dtml-in>
  </dtml-in>
</SELECT>

Note you need to use a special syntax to test the sequence-item variable because of naming conventions.

<dtml-if "_['sequence-item']=='foo'">

Example:

<SELECT NAME="myList:list" SIZE="5" MULTIPLE>
 <dtml-in valid_roles>
  <dtml-if "_['sequence-item'] != 'Anonymous'">
   <dtml-if "_['sequence-item'] != 'Shared'">
    <OPTION VALUE="<dtml-var sequence-item html_quote>">
    <dtml-var sequence-item></OPTION>
   </dtml-if>
  </dtml-if>
 </dtml-in>
</SELECT>

dtml-if tests can check values against a list:

<SELECT name="myList:list" size="5" multiple>
 <dtml-in valid_roles>
  <dtml-if "_['sequence-item'] not in ('Anonymous', 'Shared')">
   <OPTION value="<dtml-var sequence-item html_quote>">
   <dtml-var sequence-item></OPTION>
  </dtml-if>
 </dtml-in>
</SELECT>

Filling lists from properties

Create a property with type lines. For instance, you can assign this property to the folder that holds your form. Fill in several lines item for testing. Let's call this property my_subjects. Back in the form, enter the following code:

<SELECT NAME="myList">
 <dtml-in my_subjects>
   <OPTION VALUE="<dtml-var sequence-index>">
   <dtml-var sequence-index>: 
   <dtml-var sequence-item></OPTION>
 </dtml-in>
</SELECT>

sequence-index is the line number (1st line is 0). sequence-item is the actual line text. The code described above will create a list with the following content :

0: Your 1st line
1: Your 2nd line
...

Filling lists from TinyTables

TinyTable is a product that allows you to create table objects in Zope. You can download it from here.

After installing the product, create a new TinyTable (choose TinyTable in the object list). Enter an ID (let's use myTable) and column names (subject_id and subject_name, for instance). In the Data area, enter a few lines in comma-separated format: on each line, enter an id and a name as in the following example:

"my_id_1", "This is subject 1"
"my_id_2", "This is subject 2"
...

Click on the View tab to check your entry. You should see a nice table with your data.

Back in your form, enter the following code:

<SELECT NAME="myList">
 <dtml-in myTable>
   <OPTION VALUE="<dtml-var subject_id>">
   <dtml-var subject_name></OPTION>
 </dtml-in>
</SELECT>

That's all: you get a selection list based on your TinyTable. So you can easily avoid hard-coding list options in your forms.

Filling lists from SQL methods

Believe it or not, this is easy. You'll need a working database, database connection and SQL query. You'll find below a very short summary of the process. For more information, see the Z SQL Methods User's Guide. There are also several howtos about ZSQL Methods.

  1. You'll need a database. If you want to access an ODBC database on Win32, you'll need to set up an ODBC Data source first in your system.
  2. First create a database connection in your folder. I used the Zope ODBC Database Adapter Product; you can download it from here.
  3. Then create a SQL Method linked to your database connection. Let's call it mySQLQuery. Here is very basic code I used for testing; it doesn't take any argument because it returns the whole content of the table:
SELECT * FROM myTable

Let's imagine your table (myTable) has two columns, Field1 and Field2. This simple code fills up your list from your table through the SQL query:

<SELECT name="myList">
 <dtml-in mySQLQuery>
  <OPTION VALUE='<dtml-var Field1 null="">'>
  <dtml-var Field2 null="">
 </dtml-in>
</SELECT>

Filling lists from DTML methods

You can generate list data in a DTML method and pass it to the selection list in a form. The code is a bit more complex because you need to define a variable to store the data. [Todo: Is there a better way? Please fill in.]

First example: passing one variable

Create a DTML Method called myMethod and enter the following code:

<dtml-call "REQUEST.set('myOptionList',[])">
<dtml-in "objectValues(['News', 'DTML Document'])">
 <dtml-call "myOptionList.append(title_or_id)">
</dtml-in>
<dtml-return myOptionList>

myOptionList is a Python list. The 1st dtml-call tag creates an empty list. The second call appends a value (titles or IDs for DTML documents or News document in the current directory) to the list. The list is then returned by the method.

In your form, enter the following code for the selection list:

<SELECT NAME="myList" multiple>
 <dtml-in myMethod>
  <OPTION value="<dtml-var sequence-index>">
   <dtml-var sequence-index>: 
   <dtml-var sequence-item></OPTION>
 </dtml-in>
</SELECT>

The values displayed in the list will look like 1: My title or id.

Second example: passing two or more variables

Suppose I need to pass several properties per document listed by the method? I guess we can store them as lists within a list. Here's how.

In myMethod, enter the following code:

<dtml-call "REQUEST.set('myOptionList',[])">
<dtml-in "objectValues(['DTML Document'])">
 <dtml-call "REQUEST.set('myIDTitle',[id(), title_or_id()])">
 <dtml-call "myOptionList.append(myIDTitle)">
</dtml-in>
<dtml-return myOptionList>

Here we loop on DTML document objects in the current directory, we retrieve their IDs and titles, we store both properties in a list (myIDTitle), then we store these lists in a larger list (myOptionList) which is returned by the method.

In your form, enter the following code:

<SELECT NAME="myList" multiple>
 <dtml-in myMethod>
  <dtml-in sequence-item>
   <dtml-if "_['sequence-index']==0">
    <OPTION "<dtml-var sequence-item>">
   <dtml-else>
    <dtml-var sequence-item></OPTION>
   </dtml-if>
  </dtml-in>
 </dtml-in>
</SELECT>

The first dtml-in tag loops on the 1st, larger Python list. For each option line in the selection list, it extract a smaller, two-item list (stored in the first sequence-item). The second dtml-in tag loops on this smaller list. If list index is 0 (1st item, i.e. the document ID), the value is used for the OPTION tag. If list index is 1 (2nd item, i.e. the document title), the value is used for the displayed list text.

[I suppose there is a better way. How can I access a list within a list? Could I pass the data in another structure, a dictionary for instance?]

Filling lists from External methods

External methods are Python code module that are stored in the Extensions subdirectory of your Zope server. With External methods, you can create complex scripts and call them from your DTML pages. For more information, see the Using External Methods Howto.

You can fill a selection list with data returned by an external method. Here's how:

  1. In your yourZopeDir/Extensions directory, create a new text file called demo.py.

  2. In this file, enter the following code:
    def countTo(fromNumber=10, toNumber=100, myStep=10):
        """Returns a list of numbers."""
        myList=[]
        for i in range(fromNumber, toNumber, myStep):
            myList.append(i)
        return myList
    

    This simple example returns a list of numbers generated by a for loop. The loop runs from fromNumber to toNumber by myStep.

  3. In Zope, create a news External Method. In Id, enter myExternalMethod. In Function name, enter countTo. This is the name of the function or sub in the Python file. In Python module file, enter demo. This is the file name (without the .py extension). Click Edit to save your changes.

  4. In your form, enter the following code:
    <SELECT NAME="myList">
     <dtml-in myExternalMethod>
      <OPTION "<dtml-var sequence-index>">
       <dtml-var sequence-item></OPTION>
     </dtml-in>
    </SELECT>
    

    Note we use the same code as with properties or TinyTables. This will create a selection list based on the default parameters that are coded in the Python file.

  5. We can also pass parameters to the Python module from the form. Here's how:
    <SELECT NAME="myList">
     <dtml-in "myExternalMethod(50, 100, 5)">
      <OPTION "<dtml-var sequence-index>">
       <dtml-var sequence-item></OPTION>
     </dtml-in>
    </SELECT>
    

    Here we get a list with values between 50 and 100 incremented by 5. Note we use quotes in the dtml-in tag when passing parameters.

Filling lists from Python methods

PythonMethods is a product that allows you to create Python script objects from the Zope Web UI and to call them from any other object. With this product you can use safe Python scripts in your server; you don't need file access on the server to create or edit the scripts. You can download the product from here.

When you have installed the product, create a new Python method: in the Available Objects list, choose Python Method. In the Id field, enter myPythonMethod. In the Parameter list, enter:

fromNumber=10, toNumber=100, myStep=10

In the Python function body area, enter the following code:

"""Returns a list of numbers."""
myList=[]
for i in range(fromNumber, toNumber, myStep):
   myList.append(i)
return myList

Note I just reused the example from the External Method section of this howto.

Save and close the object. Reopen it and click on the Try It tab to test it. The method should return the following data based on the parameter default values:

[10, 20, 30, 40, 50, 60, 70, 80, 90]

In your form, enter the following code:

<SELECT NAME="myList">
 <dtml-in "myPythonMethod(100, 200, 10)">
  <OPTION "<dtml-var sequence-index>">
   <dtml-var sequence-item></OPTION>
 </dtml-in>
</SELECT>

You get a selection list with values between 100 and 200 incremented by 10. Remember to use quotes in the dtml-in tag when passing parameters.

Retrieving list selections

Simple selection

To retrieve the selection your form can call a handler method.

  • If your specify a value in the OPTION tag, this value is returned when you select an option in the list.
  • If your don't specify a value, the displayed option text is returned instead.

Here is an example from myForm:

<FORM action="myHandlerMethod">

<P><SELECT NAME="myList1">
  <OPTION VALUE="1">Choice 1</OPTION>
  <OPTION VALUE="2">Choice 2</OPTION>
  <OPTION VALUE="3">Choice 3</OPTION>
</SELECT></P>

<P><SELECT NAME="myList2">
  <OPTION>Choice 1</OPTION>
  <OPTION>Choice 2</OPTION>
  <OPTION>Choice 3</OPTION>
</SELECT></P>

<INPUT type="submit" name="submit" value=" OK ">
</FORM>

If your select Choice 2 in both lists, myList1 will return 2 and myList2 will return Choice 2.

In myHandlerMethod, you can catch the return values with simple <dtml-var myList1> and <dtml-var myList2> tags. You don't need to specify an object, since the result values are stored in the REQUEST object which is implicit.

Multiple selection

If you use the multiple attribute to allow multiple selections, by default the list returns a Python list of values in the [value1, value2, value3, ...] format.

<FORM action="myHandlerMethod">

<P><SELECT NAME="myList1" multiple size="3">
  <OPTION VALUE="1">Choice 1</OPTION>
  <OPTION VALUE="2">Choice 2</OPTION>
  <OPTION VALUE="3">Choice 3</OPTION>
</SELECT></P>

<P><SELECT NAME="myList2" multiple size="3">
  <OPTION>Choice 1</OPTION>
  <OPTION>Choice 2</OPTION>
  <OPTION>Choice 3</OPTION>
</SELECT></P>

<INPUT type="submit" name="submit" value=" OK ">
</FORM>

If you select the last two items, the lists return ['2', '3'] and ['Choice 2', 'Choice 3'] respectively.

Note that myList1 returns quoted figures because the return type defaults to string. You can specify a type in the name attribute as in NAME="myList1:int" so that value are automatically converted. Now myList1 returns [1, 2]. Note the values aren't quoted any more.

You can specify other types: tuple (you get ('Choice 1', 'Choice 2', 'Choice 3')), etc. For more information, see the Form Variable Types and Typechecking Howto.

If you want to process or display the return list values, you can loop on them in the handler method. Example:

<OL>
<dtml-in myList2>
 <LI><dtml-var sequence-item>
</dtml-in>
</OL>

Note that string values cannot be used with a dtml-in tag (integer values are OK). You'll get an error if you don't specify a type such as NAME="myList2:list" in the form.

Useful lists

Here are a series of code snippets I copied from existing products. Most of them were shamelessly stolen from the Zope UI pages. All lists have tested OK at least once on my box. I do not really grok them all; however, I think these code cuts can be useful as a starting point when experimenting.

List of permission settings

<SELECT name="myList:list">
 <dtml-in permission_settings mapping>
  <OPTION VALUE="<dtml-var name html_quote>">
  <dtml-var name></OPTION>
 </dtml-in>
</SELECT>

Another example where permissions acquired are preselected.

<SELECT name="myList:list" multiple size=10>
<dtml-in permission_settings mapping>
  <OPTION <dtml-if acquire>SELECTED</dtml-if>>
  <dtml-var name></OPTION>
</dtml-in>
</SELECT>

[I do not grok the mapping bit. Can anyone explain?]

List of possible permissions

<SELECT name=myList>
 <dtml-in possible_permissions>
  <OPTION><dtml-var sequence-item></OPTION>
 </dtml-in>
</SELECT>

List of user roles

<SELECT NAME="myList:list">
 <dtml-in valid_roles>
   <OPTION value="<dtml-var sequence-item html_quote>">
    <dtml-var sequence-item></OPTION>
 </dtml-in valid_roles>
</SELECT>

Another example where two roles are excluded from the list:

<SELECT NAME="myList:list" SIZE="5" MULTIPLE>
  <dtml-in valid_roles>
   <dtml-if "_['sequence-item'] != 'Anonymous'">
    <dtml-if "_['sequence-item'] != 'Shared'">
     <OPTION VALUE="<dtml-var sequence-item html_quote>">
     <dtml-var sequence-item>
    </dtml-if>
   </dtml-if>
  </dtml-in valid_roles>
</SELECT>

List of user IDs

This code displays a list of valid user IDs in the current directory:

<SELECT name="myList" size="5">
 <dtml-in get_valid_userids>
  <OPTION value="<dtml-var sequence-item html_quote>">
   <dtml-var sequence-item></OPTION>
 </dtml-in>
</SELECT>

List of roles that have a permission

This code preselects roles that have the permission "Add Folders" in the current directory. In my server, it preselected "Manager".

<SELECT name=myList:list multiple size=10>
<dtml-in expr="rolesOfPermission('Add Folders')" mapping=1> 
 <OPTION <dtml-var selected>>
 <dtml-var name></OPTION>
</dtml-in>
</SELECT>

List of permissions linked to a role

This code preselects the permissions associated with a role (Manager here).

<select name=myList:list multiple size=10>
<dtml-in expr="permissionsOfRole('Manager')" mapping=1>
 <option <dtml-var selected>>
 <dtml-var name></option>
</dtml-in>
</select>

List of all object types

<SELECT NAME="myList:list" SIZE="4" MULTIPLE>
 <OPTION VALUE="all" SELECTED> All types</OPTION>
 <dtml-in all_meta_types mapping>
  <OPTION VALUE="<dtml-var name html_quote>">
  <dtml-var name></OPTION>
 </dtml-in>
</SELECT>

List of allowed object types

Filtered list of allowed object types for the current user sorted by object name:

<SELECT NAME="myList:list">
 <dtml-in "filtered_meta_types(AUTHENTICATED_USER)" 
 mapping sort=name>
  <OPTION value="<dtml-var action fmt="url-quote">">
   <dtml-var name>"></OPTION>
 </dtml-in>
</SELECT>

Here the value is an URL to the forms that allow to create the corresponding object.

List of object IDs from the current directory

Note that one type is excluded here:

<SELECT name="myList">
 <dtml-in objectItems>
   <dtml-if "meta_type != 'Principia Factory'">   
     <OPTION><dtml-var sequence-key></OPTION>
   </dtml-if>
  </dtml-in>
</SELECT>

List of searchable objects

On my box, this code cut returns the name of my Catalog and of Confera and Squishdot top objects.

<SELECT name="myList:list" size=4 multiple>
 <dtml-in ZQueryIds>
  <OPTION><dtml-var sequence-item></OPTION>
 </dtml-in>
</SELECT>

List of SQL connections

AFAIK, this code lists database connections. It listed a Z ODBC Database Connexion ID in my server.

<select name="myList">
  <dtml-in SQLConnectionIDs>
  <option value="<dtml-var sequence-item html_quote>">
  <dtml-var sequence-key></option>
  </dtml-in>
</SELECT>

To do

  • Add information about selection and multiple selection properties; add information about selection lists in ZClasses. (Or start a new howto for these specialized issues.)