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@zope.org > http://mail.zope.org/mailman/listinfo/zope3-users _______________________________________________ Zope3-users mailing list Zope3-users@zope.org http://mail.zope.org/mailman/listinfo/zope3-users