Re: [Zope3-Users] MultiWidget
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. 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). 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). 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', _(uan item)) title = translate(title, context=self.request) return button_label % title @button.buttonAndHandler(_(uAdd 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, uRemove 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
Re: [Zope3-Users] MultiWidget
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', _(uan item)) title = translate(title, context=self.request) return button_label % title @button.buttonAndHandler(_(uAdd 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, uRemove selected items), name
[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? 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, default=button_label) title = getattr(self.field.value_type, 'title', _(uan item)) title = translate(title, context=self.request) return button_label % title @button.buttonAndHandler(_(uAdd an item), name = add, condition = lambda form: form.needAdd(), ) def handleAdd(self, action): self.appendAddingWidget() 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 else: return len(self._value) max_length @button.buttonAndHandler(_(remove-selected-items, uRemove 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): # 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() @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) Regards, Christian ___ Zope3-users mailing list Zope3-users@zope.org http://mail.zope.org/mailman/listinfo/zope3-users