Skip to content

Commit

Permalink
Feature: add an API to generate radio button elements separately.
Browse files Browse the repository at this point in the history
  • Loading branch information
alga committed Nov 27, 2013
1 parent a8ff445 commit 8092adb
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 31 deletions.
12 changes: 12 additions & 0 deletions src/z3c/form/browser/radio.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import zope.schema
import zope.schema.interfaces
from zope.i18n import translate
from zope.pagetemplate.interfaces import IPageTemplate

from z3c.form import interfaces, util
from z3c.form.widget import SequenceWidget, FieldWidget
Expand All @@ -39,6 +40,17 @@ class RadioWidget(widget.HTMLInputWidget, SequenceWidget):
def isChecked(self, term):
return term.token in self.value

def renderForValue(self, value):
term = self.terms.getTermByToken(value)

This comment has been minimized.

Copy link
@mgedmin

mgedmin Nov 27, 2013

Member

There seems to be some confusion between value and token here? Maybe it's fine ("value" in the sense of <input type="checkbox" value=...), but there's space for confusion, so the docs should be explicit.

This comment has been minimized.

Copy link
@strichter

strichter Nov 27, 2013

Contributor

Good point. I agree that some clarification in the documentation will help.

This comment has been minimized.

Copy link
@pbauer

pbauer Mar 28, 2014

Member

When value is '--NOVALUE--' (self.noValueToken) this fails with:

  File "/Users/philip/workspace/dgbbw/src-mrd/z3c.form/src/z3c/form/browser/radio.py", line 45, in renderForValue
    term = self.terms.getTermByToken(value)
  File "/Users/philip/workspace/dgbbw/src-mrd/z3c.form/src/z3c/form/term.py", line 38, in getTermByToken
    return self.terms.getTermByToken(token)
  File "/Users/philip/.cache/buildout/eggs/zope.schema-4.2.2-py2.7.egg/zope/schema/vocabulary.py", line 133, in getTermByToken
    raise LookupError(token)
LookupError: --NOVALUE--

This comment has been minimized.

Copy link
@strichter

strichter Mar 28, 2014

Contributor

Yeah, I can totally see that. I guess we had not considered NOVALUE. The fix should be pretty easy. NOVALUE has to be handled separately.

This comment has been minimized.

Copy link
@ida

ida Apr 30, 2014

@strichter: We just got hit by this in conjunction with Solgema.fullcalendar, see #45. Do you by any chance have a concrete suggestion on how to resolve this? TIA, ida

This comment has been minimized.

Copy link
@bryanlandia

bryanlandia Jun 24, 2014

I also had to pin z3c.form back to 3.0.3 (though maybe up to 3.0.5 will work) to avoid the NOVALUE errors on a radio input widget for a Plone Dexterity widget (plone.formwidget.contenttree). I'd be interested in seeing this fixed but not feeling confident to handle myself.

This comment has been minimized.

Copy link
@datakurre

datakurre Aug 6, 2014

I added the following patch for myself, but cannot make a pull, because of too long local corporate process to get the contribution agreement right now.

diff --git a/src/z3c/form/browser/radio.py b/src/z3c/form/browser/radio.py
index f909693..0d62f2d 100644
--- a/src/z3c/form/browser/radio.py
+++ b/src/z3c/form/browser/radio.py
@@ -21,6 +21,7 @@ import zope.component
 import zope.interface
 import zope.schema
 import zope.schema.interfaces
+from zope.schema.vocabulary import SimpleTerm
 from zope.i18n import translate
 from zope.pagetemplate.interfaces import IPageTemplate

@@ -41,9 +42,18 @@ class RadioWidget(widget.HTMLInputWidget, SequenceWidget):
         return term.token in self.value

     def renderForValue(self, value):
-        term = self.terms.getTermByToken(value)
+        terms = list(self.terms)
+        try:
+            term = self.terms.getTermByToken(value)
+        except LookupError:
+            if value == SequenceWidget.noValueToken:
+                term = SimpleTerm(value)
+                terms.insert(0, term)
+            else:
+                raise
+
         checked = self.isChecked(term)
-        id = '%s-%i' % (self.id, list(self.terms).index(term))
+        id = '%s-%i' % (self.id, terms.index(term))
         item = {'id': id, 'name': self.name, 'value': term.token,
                 'checked': checked}
         template = zope.component.getMultiAdapter(
checked = self.isChecked(term)
id = '%s-%i' % (self.id, list(self.terms).index(term))
item = {'id': id, 'name': self.name, 'value': term.token,
'checked': checked}
template = zope.component.getMultiAdapter(
(self.context, self.request, self.form, self.field, self),
IPageTemplate, name=self.mode + '_single')
return template(self, item)

def update(self):
"""See z3c.form.interfaces.IWidget."""
super(RadioWidget, self).update()
Expand Down
17 changes: 17 additions & 0 deletions src/z3c/form/browser/radio.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ We also need to register the template for at least the widget and request:
>>> zope.component.provideAdapter(factory,
... (zope.interface.Interface, IDefaultBrowserLayer, None, None, None),
... IPageTemplate, name='input')
>>> template_single = os.path.join(
... os.path.dirname(z3c.form.browser.__file__),
... 'radio_input_single.pt')
>>> zope.component.provideAdapter(
... z3c.form.widget.WidgetTemplateFactory(template_single),
... (zope.interface.Interface, IDefaultBrowserLayer, None, None, None),
... IPageTemplate, name='input_single')

If we render the widget we only get the empty marker:

Expand Down Expand Up @@ -106,6 +113,16 @@ get rendered with a checked flag:
</span>
<input name="widget.name-empty-marker" type="hidden" value="1" />

We can also render the input elements for each value separately:

>>> print(widget.renderForValue('true'))
<input id="widget-id-0" name="widget.name" class="radio-widget"
value="true" checked="checked" type="radio" />

>>> print(widget.renderForValue('false'))
<input id="widget-id-1" name="widget.name" class="radio-widget"
value="false" type="radio" />

Check HIDDEN_MODE:

>>> template = os.path.join(os.path.dirname(z3c.form.browser.__file__),
Expand Down
7 changes: 7 additions & 0 deletions src/z3c/form/browser/radio.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,11 @@
template="radio_hidden.pt"
/>

<z3c:widgetTemplate
mode="input_single"
widget="z3c.form.interfaces.IRadioWidget"
layer="z3c.form.interfaces.IFormLayer"
template="radio_input_single.pt"
/>

</configure>
32 changes: 1 addition & 31 deletions src/z3c/form/browser/radio_input.pt
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,7 @@
tal:repeat="item view/items">
<label for=""
tal:attributes="for item/id">
<input id="" name="" class="" alt="" title=""
tabindex="" disabled="" readonly="" accesskey="" value=""
checked="" type="radio"
tal:define="checked item/checked"
tal:attributes="id item/id;
name item/name;
class view/klass;
value item/value;
style view/style;
title view/title;
lang view/lang;
onclick view/onclick;
ondblclick view/ondblclick;
onmousedown view/onmousedown;
onmouseup view/onmouseup;
onmouseover view/onmouseover;
onmousemove view/onmousemove;
onmouseout view/onmouseout;
onkeypress view/onkeypress;
onkeydown view/onkeydown;
onkeyup view/onkeyup;
disabled view/disabled;
tabindex view/tabindex;
onfocus view/onfocus;
onblur view/onblur;
onchange view/onchange;
readonly view/readonly;
alt view/alt;
accesskey view/accesskey;
onselect view/onselect;
checked python: checked and 'checked' or None"
<input tal:replace="structure python:view.renderForValue(item['value'])"
/>
<span class="label" tal:content="item/label">Label</span>
</label>
Expand Down
37 changes: 37 additions & 0 deletions src/z3c/form/browser/radio_input_single.pt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:tal="http://xml.zope.org/namespaces/tal"
tal:define="item python:args[0]"
tal:omit-tag="">
<input id="" name="" class="" alt="" title=""
tabindex="" disabled="" readonly="" accesskey="" value=""
checked="" type="radio"
tal:define="checked item/checked"
tal:attributes="id item/id;
name item/name;
class view/klass;
value item/value;
style view/style;
title view/title;
lang view/lang;
onclick view/onclick;
ondblclick view/ondblclick;
onmousedown view/onmousedown;
onmouseup view/onmouseup;
onmouseover view/onmouseover;
onmousemove view/onmousemove;
onmouseout view/onmouseout;
onkeypress view/onkeypress;
onkeydown view/onkeydown;
onkeyup view/onkeyup;
disabled view/disabled;
tabindex view/tabindex;
onfocus view/onfocus;
onblur view/onblur;
onchange view/onchange;
readonly view/readonly;
alt view/alt;
accesskey view/accesskey;
onselect view/onselect;
checked python: checked and 'checked' or None"
/>
</html>
4 changes: 4 additions & 0 deletions src/z3c/form/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,10 @@ class ISingleCheckBoxWidget(ICheckBoxWidget):
class IRadioWidget(ISequenceWidget):
"""Radio widget."""

def renderForValue(value):
"""Render a single radio button element for a given value"""

This comment has been minimized.

Copy link
@mgedmin

mgedmin Nov 27, 2013

Member

This would be the place to explain what value means.



class ISubmitWidget(IWidget):
"""Submit widget."""

Expand Down
4 changes: 4 additions & 0 deletions src/z3c/form/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,10 @@ def setupFormDefaults():
widget.WidgetTemplateFactory(getPath('radio_display.pt'), 'text/html'),
(None, None, None, None, interfaces.IRadioWidget),
IPageTemplate, name=interfaces.DISPLAY_MODE)
zope.component.provideAdapter(
widget.WidgetTemplateFactory(getPath('radio_input_single.pt'), 'text/html'),
(None, None, None, None, interfaces.IRadioWidget),
IPageTemplate, name='input_single')

# Select Widget
zope.component.provideAdapter(select.ChoiceWidgetDispatcher)
Expand Down

0 comments on commit 8092adb

Please sign in to comment.