LOAD and Client-Server component communications
When the action of a component is called via Ajax, web2py passes two HTTP headers with the request:
web2py-component-location
web2py-component-element
which can be accessed by the action via the variables:
request.env.http_web2py_component_location
request.env.http_web2py_component_element
The latter is also accessible via:
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:
web2py-component-flash
web2py-component-command
and they can be set via:
response.headers['web2py-component-flash']='....'
response.headers['web2py-component-command']='...'
or (if the action is called by a component) automatically via:
response.flash='...'
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:
def ask():
form=SQLFORM.factory(
Field('your_email', requires=IS_EMAIL()),
Field('question', requires=IS_NOT_EMPTY()))
if form.process().accepted:
if mail.send(to='admin@example.com',
subject='from %s' % form.vars.your_email,
message = form.vars.question):
response.flash = 'Thank you'
response.js = "jQuery('#%s').hide()" % request.cid
else:
form.errors.your_email = "Unable to send the email"
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
{{=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:
{{=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:
@auth.requires_signature()
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:
{{=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 .