[Zope3-Users] MultiWidget
Christian Lück
christian.lueck at ruhr-uni-bochum.de
Thu Jan 22 14:09:25 EST 2009
Christian Lück wrote:
> Hi Roger,
>
> Roger Ineichen wrote:
>> Hi Christian
>>
>>> Betreff: [Zope3-Users] MultiWidget
>>>
>>> Hi,
>>>
>>> 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?
>> Try to call updateActions *again* after action.execute get called.
>>
>> def update(self):
>> self.updateWidgets()
>> self.updateActions()
>> self.actions.execute()
>> # update action conditions they get probably changed by execute
>> self.updateActions()
>>
>
> thanks for your answer. I think it has put me on the right track. But
> I've not arrived yet.
>
> With calling self.updateActions() on the end of the update process the
> conditions are correctly handled. Great!
> But I've run into new problems with the actions now. I'm totally stuck
> there and doen't even know how to describe the problem well. Sorry, it
> is more a phenemological description than a good analysis... Hm, the
> actions are not correctly bound to the button events.
Ok, here comes the analysis:
> 1) With the update-method you suggested: When the remove-button is
> present, the add-button is not bound to the add-action. Information on
> all other multiwidgets in the form is lost (zero subwidgets).
The reason for losing all info is simply that the update-method of
z3c.form.widget.Widget is never called.
> 2) Alternatively I've tried
>
> def update(self):
> super(MyMultiWidget, self).update()
> self.updateActions()
>
> This seems better: We don't loose information on other multiwidgets in
> the form any more. But now the remove-button is not bound to the
> remove-action (while adding works fine).
>
There is a button but no action. I guess it is still a runtime problem.
The actions, too, are created early in the setup process and the
condition for the button fails at this time. So there is probably no
action created. I guess there problem occurs as a consequence of line
260 of z3c.form/z3c/form/button.py:
Only if the condition is fullfilled an action is created.
I played around with the code and changed continue to pass. But that
then, I get a button all time, even if the condition fails. Arrg
What should I do?
>
> I also noticed that checking the length of the widgets-attribute instead
> of the value_-attribute is the way to go. For no value is added on an
> add-action but a widget. (And I noticed that min_length is not an
> attribute of the field.value_type but of the field-attribute itself.) So
> the code is now
>
> 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,
> default=button_label)
> 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):
> self.appendAddingWidget()
>
> def needAdd(self):
> max_length = getattr(self.field, 'max_length', None)
> if max_length is None:
> return True
> else:
> return len(self.widgets) < max_length
>
> @button.buttonAndHandler(_("remove-selected-items",
> 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.request]
> self.value = [widget.value for widget in self.widgets]
>
> def needRemove(self):
> min_length = getattr(self.field, 'min_length', 0)
> return len(self.widgets) > 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 update(self):
> # remove-button never tied to the remove action
> super(MultiWidget, self).update()
> # update action conditions they get probably changed by execute
> self.updateActions()
>
> def updateOFF(self):
> # loose all information on other multi widgets in the form
> # when remove-button pressed; add-button not tied to the
> # add-action when a remove button is present
> self.updateWidgets()
> self.updateActions()
> self.actions.execute()
> # update action conditions they get probably changed by execute
> self.updateActions()
>
>
> @zope.interface.implementer(interfaces.IFieldWidget)
> 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)
>
>
>> btw, I will review the form update part again because
>> I think we need more hooks if it comes to update widgets,
>> actions and conditions.
>>
>> I think your usecase is very good starting point for take
>> another look at this part.
>>
>> Probably a possible new hooks could be updateActionCondition()
>> which get called after action.execute()
>>
>
> Sounds good!
>
> Regards,
> Christian
> _______________________________________________
> Zope3-users mailing list
> Zope3-users at zope.org
> http://mail.zope.org/mailman/listinfo/zope3-users
More information about the Zope3-users
mailing list