the new MultiWidget in z3c.form is great! I like the way input errors
are reported. Thank you for this!

To help the users of my app a little I was thinking about conditions for
the the add and remove buttons. I think the old tuple-sequence widget in
zope.app.form had such a feature.  I want the add button to appear only
if the number of subwidgets is below the max_length in the schema. And
the remove button (and even the select boxes) should only appear if the
number of subwidgets exceeds the min_length of the schema or zero. --
Well, OK, one might run into an inregular state when selecting several
widgets for removal so that the min_length is violated. But, at least
there should not be a remove button if the number of subwidgets equals
zero, and no add button should appear if the number of subwidgets equals
(or exceeds) max_length.

To achive this I started with conditions for the buttons. But the
problem is that the button conditions are checked very early during the
setup of the widget when one can't yet get the number of subwidgets form
'_value'-attribute or 'widgets'-attribute of the MultiWidget instance.
So the more general question is: How would one check button conditions
that depend on a widget's state (number of subwidgets, values...)?

Do you have you a hint for me?

I code I was trying:

import zope.interface
from z3c.form import widget, button, interfaces
from z3c.form.browser import multi
from zope.i18n import translate
import zope.i18nmessageid

_ = zope.i18nmessageid.MessageFactory("zope")

class MultiWidget(multi.MultiWidget):

    buttons = button.Buttons() # reset buttons

    showLabel = False

    def addButtonLabel(self):
        button_label = _('Add %s')
        button_label = translate(button_label, context=self.request,
        title = getattr(self.field.value_type, 'title', _(u"an item"))
        title = translate(title, context=self.request)
        return button_label % title

    @button.buttonAndHandler(_(u"Add an item"),
                             name = "add",
                             condition = lambda form: form.needAdd(),
    def handleAdd(self, action):

    def needAdd(self):
        # TODO: this gets called before the widgets are updated and we
        # still have self._value == []. So always True is returned
        max_length = getattr(self.field.value_type, 'max_lenght', None)
        if max_length is None:
            return True
            return len(self._value) < max_length

                               u"Remove selected items"),
                             name = "remove",
                             condition = lambda form: form.needRemove(),
    def handleRemove(self, action):
        """see z3c.form.browser.multi.MultiWidget.handleRemove()"""
        self.widgets = [widget for widget in self.widgets
                        if ('%s.remove' % (widget.name)) not in
        self.value = [widget.value for widget in self.widgets]

    def needRemove(self):
        # TODO: This gets called before the widgets are updated and
        # len(self._value) == 0.
        min_length = getattr(self.field.value_type, 'min_length', 0)
        return len(self._value) > min_length

    def updateActions(self):
        """Use as a hook to make a nice add button label."""
        self.buttons['add'].title = self.addButtonLabel()
        super(MultiWidget, self).updateActions()

def multiFieldWidgetFactory(field, request):
    return widget.FieldWidget(field, MultiWidget(request))

@zope.interface.implementer (interfaces.IFieldWidget)
def MultiFieldWidget(field, value_type, request):
    return multiFieldWidgetFactory(field, request)

Zope3-users mailing list

Reply via email to