============
AJAX Helpers
============

The formjs package also sports utilities for working with AJAX queries.  These
utilities are provided by the ``ajax`` module.

  >>> from z3c.formjs import ajax


AJAX Request Handlers
---------------------

AJAX requests are sent from a client-side JavaScript script to the web server.
The request may contain form data or any other request data and the server
sends back a response based on the request.  The functionality for handling
requests and returning responses is already handled by browser views.  But
browser views can be a bit overkill for handling very simple requests and
responses that don't necessarily involve rendering full page templates.  The
``ajax`` module allows you to quickly build short ajax request handlers into
your form.

We will first do the necessary setup steps:

  >>> from z3c.form.testing import setupFormDefaults
  >>> setupFormDefaults()

Now we will create a simple form with an AJAX request handler.

  >>> from z3c.form import form
  >>> from z3c.formjs import interfaces
  >>> import zope.interface
  >>> class PingForm(ajax.AJAXRequestHandler, form.Form):
  ...
  ...     @ajax.handler
  ...     def pingBack(self):
  ...         message = self.request.get('message', 'Nothing to ping back.')
  ...         return "from %r: %s" % (self, message)

The ``AJAXRequestHandler`` class provides the ``IAJAXRequestHandler``
interface.  This means that the ``PingForm`` class will have an
``ajaxRequestHandlers`` selection manager.  When you use the ``@ajax.handler``
decorator, the decorated function gets registered in the AJAX handlers manager
and is converted to an ``AJAXHandler`` instance.

  >>> from z3c.form.testing import TestRequest
  >>> request = TestRequest()
  >>> ping = PingForm(None, request)
  >>> ping.ajaxRequestHandlers
  <AJAXHandlers ['pingBack']>
  >>> ping.pingBack
  <AJAXHandler 'pingBack'>
  >>> ping.ajaxRequestHandlers['pingBack']
  <AJAXHandler 'pingBack'>

Since the form or anything else of interest is not around during the creation
of the ``AJAXHandler`` instance, we have to wrap the handler once the other
components are available. When executing the AJAX handler itself, you must
pass in the form explicitely:

  >>> print ping.pingBack(ping)
  from <PingForm ...>: Nothing to ping back.

Before the handler is called we put an ``AJAXView`` class around it:

  >>> pingView = ajax.AJAXView(ping.pingBack, ping.request, ping)

The ping AJAX view can simply be called:

  >>> print pingView()
  from <PingForm ...>: Nothing to ping back.

To hook up the AJAX handler as a public URL, we use a pluggable traverser
traversal plugin. Let's first instantiate a pluggable traverser providing our
form as the context.

  >>> from z3c.traverser.browser import PluggableBrowserTraverser
  >>> traverser = PluggableBrowserTraverser(ping, request)
  >>> traverser.publishTraverse(request, 'pingBack')()
  Traceback (most recent call last):
  ...
  NotFound: Object: <PingForm object at ...>, name: 'pingBack'

We have not yet registered a plugin for the pluggable traverser.  We will
register the ``AJAXRequestTraverserPlugin`` which will only traverse to
objects stored in the ``ajaxRequestHandlers`` selection manager.

  >>> import zope.component
  >>> from z3c.traverser.interfaces import ITraverserPlugin
  >>> from zope.publisher.interfaces.browser import IBrowserRequest
  >>> zope.component.provideSubscriptionAdapter(
  ...     ajax.AJAXRequestTraverserPlugin,
  ...     (interfaces.IFormTraverser, IBrowserRequest),
  ...     provides=ITraverserPlugin)

Now we will try traversing to our handler again.

  >>> print traverser.publishTraverse(request, 'pingBack')()
  from <PingForm ...>: Nothing to ping back.

When providing a message, then the pin back is less silly:

  >>> request = TestRequest(form={'message': 'hello'})
  >>> ping = PingForm(None, request)
  >>> traverser = PluggableBrowserTraverser(ping, request)
  >>> print traverser.publishTraverse(request, 'pingBack')()
  from <PingForm ...>: hello


NOTE: The pluggable traverser itself can be registered in a number of ways.
But the best way is to register it as a view for the from in question.  Since
forms generally inherit from the ``z3c.form.form.BaseForm`` object, which
itself inherits from BrowserPage, most forms will already have a
publishTraverse method which will override any attempt to adapt to a diferent
traverser.  But if you provide the pluggable traverser as a view on the form,
then using the @@ symbols to force a view lookup rather than a publishTraverse
call will bypass BrowserPage's publishTraverse method.  In ZCML, the pluggable
traverser gets registered as a named adatper like so:

  <adapter
      trusted="True"
      for=".interfaces.IFormTraverser
	   zope.publisher.interfaces.browser.IBrowserRequest"
      provides="zope.publisher.interfaces.browser.IBrowserPublisher"
      factory="z3c.traverser.browser.PluggableBrowserTraverser"
      permission="zope.Public"
      name="ajax"
      />

This makes the plggable traverser available via the @@ajax "view".
In a url, an ajax request handler would be called via the url:
http://host/path/to/context/@@form.html/@@ajax/pingBack
