=============
Select Widget
=============
The select widget allows you to select one or more values from a set of given
options. The "SELECT" and "OPTION" elements are described here:
http://www.w3.org/TR/1999/REC-html401-19991224/interact/forms.html#edef-SELECT
As for all widgets, the select widget must provide the new ``IWidget``
interface:
>>> from zope.interface.verify import verifyClass
>>> from z3c.form import interfaces
>>> from z3c.form.browser import select
>>> verifyClass(interfaces.IWidget, select.SelectWidget)
True
The widget can be instantiated only using the request:
>>> from z3c.form.testing import TestRequest
>>> request = TestRequest()
>>> widget = select.SelectWidget(request)
Before rendering the widget, one has to set the name and id of the widget:
>>> widget.id = 'widget-id'
>>> widget.name = 'widget.name'
We also need to register the template for at least the widget and request:
>>> import zope.component
>>> from zope.pagetemplate.interfaces import IPageTemplate
>>> from z3c.form.testing import getPath
>>> from z3c.form.widget import WidgetTemplateFactory
>>> zope.component.provideAdapter(
... WidgetTemplateFactory(getPath('select_input.pt'), 'text/html'),
... (None, None, None, None, interfaces.ISelectWidget),
... IPageTemplate, name=interfaces.INPUT_MODE)
If we render the widget we get an empty widget:
>>> print(widget.render())
Let's provide some values for this widget. We can do this by defining a source
providing ``ITerms``. This source uses descriminators which will fit our setup.
>>> import zope.schema.interfaces
>>> from zope.schema.vocabulary import SimpleVocabulary
>>> import z3c.form.term
>>> class SelectionTerms(z3c.form.term.Terms):
... def __init__(self, context, request, form, field, widget):
... self.terms = SimpleVocabulary.fromValues(['a', 'b', 'c'])
>>> zope.component.provideAdapter(SelectionTerms,
... (None, interfaces.IFormLayer, None, None, interfaces.ISelectWidget) )
Now let's try if we get widget values:
>>> widget.update()
>>> print(widget.render())
If we select item "b", then it should be selected:
>>> widget.value = ['b']
>>> widget.update()
>>> print(widget.render())
Let's see what happens if we have values that are not in the vocabulary:
>>> widget.value = ['x', 'y']
>>> widget.update()
>>> print(widget.render())
Let's now make sure that we can extract user entered data from a widget:
>>> widget.request = TestRequest(form={'widget.name': ['c']})
>>> widget.update()
>>> widget.extract()
['c']
When "No value" is selected, then no verification against the terms is done:
>>> widget.request = TestRequest(form={'widget.name': ['--NOVALUE--']})
>>> widget.update()
>>> widget.extract(default=1)
['--NOVALUE--']
Unfortunately, when nothing is selected, we do not get an empty list sent into
the request, but simply no entry at all. For this we have the empty marker, so
that:
>>> widget.request = TestRequest(form={'widget.name-empty-marker': '1'})
>>> widget.update()
>>> widget.extract()
[]
If nothing is found in the request, the default is returned:
>>> widget.request = TestRequest()
>>> widget.update()
>>> widget.extract(default=1)
1
Let's now make sure that a bogus value causes extract to return the default as
described by the interface:
>>> widget.request = TestRequest(form={'widget.name': ['x']})
>>> widget.update()
>>> widget.extract(default=1)
1
Custom No Value Messages
------------------------
Additionally to the standard dynamic attribute values, the select widget also
allows dynamic values for the "No value message". Initially, we have the
default message:
>>> widget.noValueMessage
u'No value'
Let's now register an attribute value:
>>> from z3c.form.widget import StaticWidgetAttribute
>>> NoValueMessage = StaticWidgetAttribute(u'- nothing -')
>>> import zope.component
>>> zope.component.provideAdapter(NoValueMessage, name='noValueMessage')
After updating the widget, the no value message changed to the value provided
by the adapter:
>>> widget.update()
>>> widget.noValueMessage
u'- nothing -'
Explicit Selection Prompt
-------------------------
In certain scenarios it is desirable to ask the user to select a value and
display it as the first choice, such as "please select a value". In those
cases you just have to set the ``prompt`` attribute to ``True``:
>>> widget.prompt = True
>>> widget.update()
>>> print(widget.render())
As you can see, even though the field is not required, only the explicit
prompt is shown. However, the prompt will also be shown if the field is
required:
>>> widget.required = True
>>> widget.update()
>>> print(widget.render())
Since the prompy uses the "No value" as the value for the selection, all
behavior is identical to selecting "No value". As for the no-value message,
the prompt message, which is available under
>>> widget.promptMessage
u'Select a value ...'
can also be changed using an attribute value adapter:
>>> PromptMessage = StaticWidgetAttribute(u'Please select a value')
>>> zope.component.provideAdapter(PromptMessage, name='promptMessage')
So after updating the widget you have the custom value:
>>> widget.update()
>>> widget.promptMessage
u'Please select a value'
Additionally, the select widget also allows dynamic value for the ``prompt``
attribute . Initially, value is ``False``:
>>> widget.prompt = False
>>> widget.prompt
False
Let's now register an attribute value:
>>> from z3c.form.widget import StaticWidgetAttribute
>>> AllowPrompt = StaticWidgetAttribute(True)
>>> import zope.component
>>> zope.component.provideAdapter(AllowPrompt, name='prompt')
After updating the widget, the value for the prompt attribute changed to the
value provided by the adapter:
>>> widget.update()
>>> widget.prompt
True
Display Widget
--------------
The select widget comes with a template for ``DISPLAY_MODE``. Let's
register it first:
>>> zope.component.provideAdapter(
... WidgetTemplateFactory(getPath('select_display.pt'), 'text/html'),
... (None, None, None, None, interfaces.ISelectWidget),
... IPageTemplate, name=interfaces.DISPLAY_MODE)
>>> widget.mode = interfaces.DISPLAY_MODE
>>> widget.value = ['b', 'c']
>>> widget.update()
>>> print(widget.render())
b,
c
Let's see what happens if we have values that are not in the vocabulary:
>>> widget.value = ['x', 'y']
>>> widget.update()
>>> print(widget.render())
Hidden Widget
-------------
The select widget comes with a template for ``HIDDEN_MODE``. Let's
register it first:
>>> zope.component.provideAdapter(
... WidgetTemplateFactory(getPath('select_hidden.pt'), 'text/html'),
... (None, None, None, None, interfaces.ISelectWidget),
... IPageTemplate, name=interfaces.HIDDEN_MODE)
We can now set our widget's mode to hidden and render it:
>>> widget.mode = interfaces.HIDDEN_MODE
>>> widget.value = ['b']
>>> widget.update()
>>> print(widget.render())
Let's see what happens if we have values that are not in the vocabulary:
>>> widget.value = ['x', 'y']
>>> widget.update()
>>> print(widget.render())