[Checkins] SVN: z3c.formjs/trunk/src/z3c/formjs/ First attempt at
making multiple handlers per element working. Still a
Stephan Richter
srichter at cosmos.phy.tufts.edu
Sat Jul 7 02:15:13 EDT 2007
Log message for revision 77556:
First attempt at making multiple handlers per element working. Still a
little clumsy and more tests are missing.
Now you can also pass form fields to handler decorator.
Changed:
U z3c.formjs/trunk/src/z3c/formjs/interfaces.py
U z3c.formjs/trunk/src/z3c/formjs/jsaction.py
U z3c.formjs/trunk/src/z3c/formjs/jsaction.txt
U z3c.formjs/trunk/src/z3c/formjs/jsevent.py
-=-
Modified: z3c.formjs/trunk/src/z3c/formjs/interfaces.py
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/interfaces.py 2007-07-07 05:45:10 UTC (rev 77555)
+++ z3c.formjs/trunk/src/z3c/formjs/interfaces.py 2007-07-07 06:15:12 UTC (rev 77556)
@@ -128,6 +128,10 @@
"""call the handler, passing it the form."""
+class IJSEventHandlers(zope.interface.Interface):
+ pass
+
+
# -----[ Validator ]--------------------------------------------------------
Modified: z3c.formjs/trunk/src/z3c/formjs/jsaction.py
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/jsaction.py 2007-07-07 05:45:10 UTC (rev 77555)
+++ z3c.formjs/trunk/src/z3c/formjs/jsaction.py 2007-07-07 06:15:12 UTC (rev 77556)
@@ -20,9 +20,11 @@
import zope.component
import zope.interface
import zope.location
-from z3c.form import button, action
+from zope.interface import adapter
+from z3c.form import button, action, util
from z3c.form.browser.button import ButtonWidget
-from z3c.form.interfaces import IFormLayer, IFieldWidget, IFormAware
+from z3c.form.interfaces import IField, IFieldWidget
+from z3c.form.interfaces import IFormLayer, IFormAware
from z3c.form.interfaces import IButtonAction, IAfterWidgetUpdateEvent
from z3c.formjs import interfaces, jsevent
@@ -66,22 +68,63 @@
def id(self):
return self.name.replace('.', '-')
- def update(self):
- super(JSButtonAction, self).update()
- # Step 1: Get the handler.
- handler = self.form.handlers.getHandler(self.field)
- # Step 2: Create a selector.
- selector = WidgetSelector(self)
- # Step 3: Make sure that the form has JS subscriptions, otherwise add
- # it.
- if not interfaces.IHaveJSSubscriptions.providedBy(self.form):
- self.form.jsSubscriptions = jsevent.JSSubscriptions()
- zope.interface.alsoProvides(
- self.form, interfaces.IHaveJSSubscriptions)
- # Step 4: Add the subscription to the form:
- self.form.jsSubscriptions.subscribe(handler.event, selector, handler)
+class JSHandlers(object):
+ """Javascript event handlers for fields and buttons."""
+ zope.interface.implements(interfaces.IJSEventHandlers)
+ def __init__(self):
+ self._registry = adapter.AdapterRegistry()
+ self._handlers = ()
+
+ def addHandler(self, field, event, handler):
+ """See interfaces.IEventHandlers"""
+ # Create a specification for the field and event
+ fieldSpec = util.getSpecification(field)
+ eventSpec = util.getSpecification(event)
+ if isinstance(fieldSpec, util.classTypes):
+ fieldSpec = zope.interface.implementedBy(fieldSpec)
+ if isinstance(eventSpec, util.classTypes):
+ eventSpec = zope.interface.implementedBy(eventSpec)
+ # Register the handler
+ self._registry.register(
+ (fieldSpec, eventSpec), interfaces.IJSEventHandler, '', handler)
+ self._handlers += ((field, event, handler),)
+
+ def getHandlers(self, field):
+ """See interfaces.IButtonHandlers"""
+ fieldProvided = zope.interface.providedBy(field)
+ handlers = ()
+ for event in jsevent.EVENTS:
+ eventProvided = zope.interface.providedBy(event)
+ handler = self._registry.lookup(
+ (fieldProvided, eventProvided), interfaces.IJSEventHandler)
+ if handler:
+ handlers += (handler,)
+ return handlers
+
+ def copy(self):
+ """See interfaces.IButtonHandlers"""
+ handlers = Handlers()
+ for button, handler in self._handlers:
+ handlers.addHandler(button, handler)
+ return handlers
+
+ def __add__(self, other):
+ """See interfaces.IButtonHandlers"""
+ if not isinstance(other, Handlers):
+ raise NotImplementedError
+ handlers = self.copy()
+ for button, handler in other._handlers:
+ handlers.addHandler(button, handler)
+ return handlers
+
+ def __repr__(self):
+ return '<JSHandlers %r>' % (
+ self.__class__.__name__,
+ [handler for button, handler in self._handlers])
+
+
class JSHandler(object):
zope.interface.implements(interfaces.IJSEventHandler)
@@ -99,12 +142,16 @@
def handler(field, **kwargs):
"""A decorator for defining a javascript event handler."""
+ # As a convenience, we also accept form fields to the handler, but get the
+ # real field immediately
+ if IField.providedBy(field):
+ field = field.field
def createHandler(func):
handler = JSHandler(field, func, **kwargs)
frame = sys._getframe(1)
f_locals = frame.f_locals
- handlers = f_locals.setdefault('handlers', button.Handlers())
- handlers.addHandler(field, handler)
+ handlers = f_locals.setdefault('jshandlers', JSHandlers())
+ handlers.addHandler(field, handler.event, handler)
return handler
return createHandler
@@ -116,9 +163,7 @@
if not (IFieldWidget.providedBy(widget) and IFormAware.providedBy(widget)):
return
# Step 1: Get the handler.
- handler = widget.form.handlers.getHandler(widget.field)
- if handler is None:
- return
+ handlers = widget.form.jshandlers.getHandlers(widget.field)
# Step 2: Create a selector.
selector = WidgetSelector(widget)
# Step 3: Make sure that the form has JS subscriptions, otherwise add
@@ -128,4 +173,5 @@
zope.interface.alsoProvides(
widget.form, interfaces.IHaveJSSubscriptions)
# Step 4: Add the subscription to the form:
- widget.form.jsSubscriptions.subscribe(handler.event, selector, handler)
+ for handler in handlers:
+ widget.form.jsSubscriptions.subscribe(handler.event, selector, handler)
Modified: z3c.formjs/trunk/src/z3c/formjs/jsaction.txt
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/jsaction.txt 2007-07-07 05:45:10 UTC (rev 77555)
+++ z3c.formjs/trunk/src/z3c/formjs/jsaction.txt 2007-07-07 06:15:12 UTC (rev 77556)
@@ -135,6 +135,11 @@
>>> zope.component.provideAdapter(
... jsaction.JSButtonAction, provides=IButtonAction)
+Finally, for the Javascript subscriptions to be registered, we need an event
+listener that reacts to "after widget/action update" events:
+
+ >>> zope.component.provideHandler(jsaction.createSubscriptionsForWidget)
+
Action managers are instantiated using the form, request, and
context/content. A button-action-manager implementation is avaialble in the
``z3c.form.button`` package:
@@ -273,12 +278,37 @@
Multiple Handlers
-----------------
-Currently it is not possible to have multiple handlers for one dom element,
-even though the event is different!!!
+Since there are multiple events in Javascript, one element can have multiple
+handlers. So let's define a new form that declares two handlers for the same
+button:
-XXX: to be done
+ >>> class Form(form.Form):
+ ... buttons = button.Buttons(IButtons).select('hello')
+ ...
+ ... @jsaction.handler(buttons['hello'])
+ ... def showHelloWorldMessage(self, selector):
+ ... return 'alert("Hello World!");'
+ ...
+ ... @jsaction.handler(buttons['hello'], event=jsevent.DBLCLICK)
+ ... def showDoubleHelloWorldMessage(self, selector):
+ ... return 'alert("Hello World! x 2");'
+Let's now instantiate and update the form:
+ >>> demoform = Form(None, request)
+ >>> demoform.update()
+
+The subscriptions are now available:
+
+ >>> list(demoform.jsSubscriptions)
+ [<JSSubscription event=<JSEvent "click">,
+ selector=<WidgetSelector "form-buttons-hello">,
+ handler=<JSHandler for <JSButton 'hello' u'Hello World!'>>>,
+ <JSSubscription event=<JSEvent "dblclick">,
+ selector=<WidgetSelector "form-buttons-hello">,
+ handler=<JSHandler for <JSButton 'hello' u'Hello World!'>>>]
+
+
Submit and Javascript Buttons Together
--------------------------------------
@@ -300,16 +330,14 @@
Even though somewhat pointless, whenever the age field is clicked on or the
name changed, we would like to get an alert:
- XXX: Ugliness! fields['age'].field --> fields['age']
-
>>> class PersonAddForm(form.AddForm):
... fields = field.Fields(IPerson)
...
- ... @jsaction.handler(fields['age'].field)
+ ... @jsaction.handler(fields['age'])
... def ageClickEvent(self, selector):
... return 'alert("The Age was Clicked!");'
...
- ... @jsaction.handler(fields['name'].field, event=jsevent.CHANGE)
+ ... @jsaction.handler(fields['name'], event=jsevent.CHANGE)
... def nameChangeEvent(self, selector):
... return 'alert("The Name was Changed!");'
@@ -326,22 +354,6 @@
>>> print addform.render()
<html>
<head>
- </head>
- ...
- </html>
-
-As you can see there were no subscriptions rendererd. This reason is that we
-have not yet registered the event listener to the ``IAfterWidgetUpdateEvent``
-event:
-
- >>> zope.component.provideHandler(jsaction.createSubscriptionsForWidget)
-
-So, let's try this again:
-
- >>> addform.update()
- >>> print addform.render()
- <html>
- <head>
<script type="text/javascript">
$(document).ready(function(){
$("#form-widgets-name").bind("change",
@@ -352,8 +364,6 @@
</script>
</head>
<body>
- <BLANKLINE>
- <BLANKLINE>
<form action=".">
<div class="row">
<label for="form-widgets-name">Name</label>
Modified: z3c.formjs/trunk/src/z3c/formjs/jsevent.py
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/jsevent.py 2007-07-07 05:45:10 UTC (rev 77555)
+++ z3c.formjs/trunk/src/z3c/formjs/jsevent.py 2007-07-07 06:15:12 UTC (rev 77556)
@@ -52,6 +52,9 @@
SELECT = JSEvent("select")
SUBMIT = JSEvent("submit")
+EVENTS = (CLICK, DBLCLICK, CHANGE, LOAD, BLUR, FOCUS, KEYDOWN, KEYUP,
+ MOUSEDOWN, MOUSEMOVE, MOUSEOUT, MOUSEOVER, MOUSEUP, RESIZE, SELECT,
+ SUBMIT)
class IdSelector(object):
zope.interface.implements(interfaces.IIdSelector)
More information about the Checkins
mailing list