LOAD and Client-Server component communications

When the action of a component is called via Ajax, web2py passes two HTTP headers with the request:

  1. web2py-component-location
  2. web2py-component-element

which can be accessed by the action via the variables:

  1. request.env.http_web2py_component_location
  2. request.env.http_web2py_component_element

The latter is also accessible via:

  1. request.cid

The former contains the URL of the page that called the component action. The latter contains the id of the DIV that will contain the response.

The component action can also store data in two special HTTP response headers that will be interpreted by the full page upon response. They are:

  1. web2py-component-flash
  2. web2py-component-command

and they can be set via:

  1. response.headers['web2py-component-flash']='....'
  2. response.headers['web2py-component-command']='...'

or (if the action is called by a component) automatically via:

  1. response.flash='...'
  2. response.js='...'

The former contains text that you want to be flashed upon response. The latter contains JavaScript code that you want to be executed upon response. It cannot contain newlines.

As an example, let’s define a contact form component in “controllers/contact/ask.py” that allows the user to ask a question. The component will email the question to the system administrator, flash a “thank you” message, and remove the component from the page:

  1. def ask():
  2. form=SQLFORM.factory(
  3. Field('your_email', requires=IS_EMAIL()),
  4. Field('question', requires=IS_NOT_EMPTY()))
  5. if form.process().accepted:
  6. if mail.send(to='admin@example.com',
  7. subject='from %s' % form.vars.your_email,
  8. message = form.vars.question):
  9. response.flash = 'Thank you'
  10. response.js = "jQuery('#%s').hide()" % request.cid
  11. else:
  12. form.errors.your_email = "Unable to send the email"
  13. return dict(form=form)

The first four lines define the form and accept it. The mail object used for sending is defined in the default scaffolding application. The last four lines implement all the component-specific logic by getting data from the HTTP request headers and setting the HTTP response headers.

Now you can embed this contact form in any page via

  1. {{=LOAD('contact', 'ask.load', ajax=True)}}

Notice that we did not define a .load view for our ask component. We do not have to because it returns a single object (form) and therefore the “generic.load” will do just fine. Remember that generic views are a development tool. In production you should copy “views/generic.load” into “views/contact/ask.load”.

We can block access to a function called via Ajax by digitally signing the URL using the user_signature argument:

  1. {{=LOAD('contact', 'ask.load', ajax=True, user_signature=True)}}

which add a digital signature to the URL. The digital signature must then be validated using a decorator in the callback function:

  1. @auth.requires_signature()
  2. def ask(): ...

Trapped Ajax links and the A Helper

Normally a link is not trapped, and by clicking in a link inside a component, the entire linked page is loaded. Sometimes you want the linked page to be loaded inside the component. This can be achieved using the A helper:

  1. {{=A('linked page', _href='http://example.com', cid=request.cid)}}

If cid is specified, the linked page is loaded via Ajax. The cid is the id of the html element where to place the loaded page content. In this case we set it to request.cid, i.e. the id of the component that generates the link. The linked page can be and usually is an internal URL generated using the URL helper .