==============================================
A technique for communication between viewlets
==============================================


Sometimes one wants viewlets to communicate with each other to supplement a
more generic viewlet's behaviour with another viewlet. One example would be a
viewlet that contains a search form created by a library such as z3c.form,
plus a second viewlet that provides a list of search results.

This is very simple to accomplish with zope.viewlet but has turned out not to
be obvious, so here is an explicit example. It is not written as a doc test
since it uses z3c.form which should not become a dependency of zope.viewlet.


The viewlets
============

For the purpose of this example, we simulate a search form. Our search results
are simply the characters of the search term and are stored on the viewlet as
an attribute:


    class ISearchForm(zope.interface.Interface):

        searchterm = zope.schema.TextLine(title=u"Search term")


    class SearchForm(z3c.form.form.Form):

        ignoreContext = True

        fields = z3c.form.field.Fields(ISearchForm)

        results = ""

        @z3c.form.button.buttonAndHandler(u"Search")
        def search(self, action):
            data, errors = self.extractData()
            self.results = list(data["searchterm"])


(Notice that this example is minimized to point out communication between
viewlets, and no care is taken to handle the form itself in the best way
possible. In particular, one will probably want to make sure that the actual
search is not performed more than once, which may happen with the above code.)

The result list viewlet needs to display the results stored on the search
form. Therefore, the result list viewlet needs to access that viewlet, which
is probably the only tricky part (if there is any at all) in this example:
Since viewlets know their viewlet manager which lets viewlets be looked up by
ID, it is all a matter of the result list viewlet knowing the ID of the search
form.

We'll store the search form viewlet's ID as an attribute of the result list
viewlet. Let's hard-code the ID for a start, then the result list looks like
this:


    class ResultList(zope.viewlet.viewlet.ViewletBase):

        searchform_id = "searchform"

        def update(self):
            super(ResultList, self).update()
            searchform = self.manager[self.searchform_id]
            searchform.update()
            self.results = searchform.results

        def render(self):
            return "<ul>%s</ul>" % "\n".join(u"<li>%s</li>" % x
                                             for x in self.results)


Registering the viewlets
========================

As long as we treat the ID of the search form as hard-coded, we have to use
the correct name when registering the two viewlets:


    <viewlet
       name="searchform"
       class="...SearchForm"
       permission="zope.Public"
       />

    <viewlet
       name="resultlist"
       class="...ResultList"
       permission="zope.Public"
       />


Making the ID of the search form more flexible now doesn't even require
changing any code: the viewlet directive may be passed arbitrary attributes
which will be available as attributes of the ResultList objects. The attribute
that holds our search form's ID is searchform_id, so we might register the
viewlets like this:


    <viewlet
       name="sitesearch"
       class="...SearchForm"
       permission="zope.Public"
       />

    <viewlet
       name="resultlist"
       class="...ResultList"
       permission="zope.Public"
       searchform_id="sitesearch"
       />
