6. Directives

6.1. Widget template directive

Show how we can use the widget template directive. Register the meta configuration for the directive.

>>> import sys
>>> from zope.configuration import xmlconfig
>>> import z3c.form
>>> context = xmlconfig.file('meta.zcml', z3c.form)

We need a custom widget template

>>> import os, tempfile
>>> temp_dir = tempfile.mkdtemp()
>>> widget_file = os.path.join(temp_dir, 'widget.pt')
>>> with open(widget_file, 'w') as file:
...     _ = file.write('''
... <html xmlns="http://www.w3.org/1999/xhtml"
...       xmlns:tal="http://xml.zope.org/namespaces/tal"
...       tal:omit-tag="">
...    <input type="text" id="" name="" value="" size=""
...           tal:attributes="id view/id;
...                           name view/name;
...                           size view/size;
...                           value view/value;" />
... </html>
... ''')

and a interface

>>> import zope.interface
>>> from z3c.form import interfaces
>>> class IMyWidget(interfaces.IWidget):
...     """My widget interface."""

and a widget class:

>>> from z3c.form.testing import TestRequest
>>> from z3c.form.browser import text
>>> @zope.interface.implementer(IMyWidget)
... class MyWidget(text.TextWidget):
...     pass
>>> request = TestRequest()
>>> myWidget = MyWidget(request)

Make them available under the fake package custom:

>>> sys.modules['custom'] = type(
...     'Module', (),
...     {'IMyWidget': IMyWidget})()

and register them as a widget template within the z3c:widgetTemplate directive:

>>> context = xmlconfig.string("""
... <configure
...     xmlns:z3c="http://namespaces.zope.org/z3c">
...   <z3c:widgetTemplate
...       template="%s"
...       widget="custom.IMyWidget"
...       />
... </configure>
... """ % widget_file, context=context)

Let’s get the template

>>> import zope.component
>>> from z3c.template.interfaces import IPageTemplate
>>> template = zope.component.queryMultiAdapter((None, request, None, None,
...     myWidget), interface=IPageTemplate, name='input')

and check it:

>>> from zope.browserpage.viewpagetemplatefile import ViewPageTemplateFile
>>> isinstance(template, ViewPageTemplateFile)
True

Let’s use the template within the widget.

>>> print(template(myWidget))
<input type="text" value="" />

We normally render the widget which returns the registered template.

>>> print(myWidget.render())
<input type="text" value="" />

If the template does not exist, then the widget directive should fail immediately:

>>> unknownFile = os.path.join(temp_dir, 'unknown.pt')
>>> context = xmlconfig.string("""
... <configure
...     xmlns:z3c="http://namespaces.zope.org/z3c">
...   <z3c:widgetTemplate
...       template="%s"
...       widget="custom.IMyWidget"
...       />
... </configure>
... """ % unknownFile, context=context)
Traceback (most recent call last):
...
ConfigurationError: ('No such file', '...unknown.pt')

6.2. Object Widget template directive

Show how we can use the objectwidget template directive.

The big difference between the ‘simple’ Widget template and the Object Widget directive is that the Object Widget template takes the field’s schema into account. That makes it easy to register different widget templates for different sub-schemas. You can use this together with SubformAdapter to get a totally custom subwidget.

We need a custom widget template

>>> widget_file = os.path.join(temp_dir, 'widget.pt')
>>> with open(widget_file, 'w') as file:
...     _ = file.write('''
... <html xmlns="http://www.w3.org/1999/xhtml"
...       xmlns:tal="http://xml.zope.org/namespaces/tal"
...       tal:omit-tag="">
...    <div class="object-widget" tal:attributes="class view/klass">
...    yeah, this can get complex
...    </div>
... </html>
... ''')

and a interface

>>> class IMyObjectWidget(interfaces.IObjectWidget):
...     """My objectwidget interface."""

and a widget class:

>>> from z3c.form.browser import object
>>> @zope.interface.implementer(IMyObjectWidget)
... class MyObjectWidget(object.ObjectWidget):
...     pass
>>> request = TestRequest()
>>> myObjectWidget = MyObjectWidget(request)
>>> from z3c.form.testing import IMySubObject
>>> import zope.schema
>>> field = zope.schema.Object(
...     __name__='subobject',
...     title=u'my object widget',
...     schema=IMySubObject)
>>> myObjectWidget.field = field

Make them available under the fake package custom:

>>> sys.modules['custom'] = type(
...     'Module', (),
...     {'IMyObjectWidget': IMyObjectWidget})()

and register them as a widget template within the z3c:objectWidgetTemplate directive:

>>> context = xmlconfig.string("""
... <configure
...     xmlns:z3c="http://namespaces.zope.org/z3c">
...   <z3c:objectWidgetTemplate
...       template="%s"
...       widget="custom.IMyObjectWidget"
...       />
... </configure>
... """ % widget_file, context=context)

Let’s get the template

>>> template = zope.component.queryMultiAdapter((None, request, None, None,
...     myObjectWidget, None), interface=IPageTemplate, name='input')

and check it:

>>> isinstance(template, ViewPageTemplateFile)
True

Let’s use the template within the widget.

>>> print(template(myObjectWidget))
<div class="object-widget">yeah, this can get complex</div>

We normally render the widget which returns the registered template.

>>> print(myObjectWidget.render())
<div class="object-widget">yeah, this can get complex</div>

If the template does not exist, then the widget directive should fail immediately:

>>> unknownFile = os.path.join(temp_dir, 'unknown.pt')
>>> context = xmlconfig.string("""
... <configure
...     xmlns:z3c="http://namespaces.zope.org/z3c">
...   <z3c:objectWidgetTemplate
...       template="%s"
...       widget="custom.IMyObjectWidget"
...       />
... </configure>
... """ % unknownFile, context=context)
Traceback (most recent call last):
...
ConfigurationError: ('No such file', '...unknown.pt')

Register a specific template for a schema:

We need a custom widget template

>>> widgetspec_file = os.path.join(temp_dir, 'widgetspec.pt')
>>> with open(widgetspec_file, 'w') as file:
...     _ = file.write('''
... <html xmlns="http://www.w3.org/1999/xhtml"
...       xmlns:tal="http://xml.zope.org/namespaces/tal"
...       tal:omit-tag="">
...    <div class="object-widget" tal:attributes="class view/klass">
...    this one is specific
...    </div>
... </html>
... ''')
>>> context = xmlconfig.string("""
... <configure
...     xmlns:z3c="http://namespaces.zope.org/z3c">
...   <z3c:objectWidgetTemplate
...       template="%s"
...       widget="custom.IMyObjectWidget"
...       schema="z3c.form.testing.IMySubObject"
...       />
... </configure>
... """ % widgetspec_file, context=context)

Let’s get the template

>>> template = zope.component.queryMultiAdapter((None, request, None, None,
...     myObjectWidget, None), interface=IPageTemplate, name='input')

and check it:

>>> print(myObjectWidget.render())
<div class="object-widget">this one is specific</div>

6.3. Cleanup

Now we need to clean up the custom module.

>>> del sys.modules['custom']

Also let’s not leave temporary files lying around

>>> import shutil
>>> shutil.rmtree(temp_dir)