Personal tools
You are here: Home Documentation Tutorials Using YUI on Grok JSON meets the Panel

JSON meets the Panel

Finally, we'll see how to use JSON for data interchange in Grok. We'll also see how to use a YUI Panel in the process.
In this tutorial you'll be learning how to use the Yahoo! User Interface Library, or YUI in short in your Grok project. YUI is a JavaScript and CSS library providing all sorts of handy components that you can use to make you site work an look better without too much effort, and in a cross-browser compatible way. In the process, you'll also be learning some basic uses of megrok.layout, megrok.navigation and JSON.
Page 5 of 6.

Flash messages

To demonstrate JSON, we'll be using flash messages. Flash messages are short messages that you can 'send' to the user from somewhere inside your code -- typically when a new object was created or something similar -- and then show them on the page rendered next. See the z3c.flashmessage package on PyPI for more info.

We'll be periodically checking the server for new messages, and display them in a Panel that will popup.

Grok 1.0 uses z3c.flashmessage by default and registers a z3c.flashmessage.interfaces.IMessageSource utility A grok.View has a flash method for this purpose, however, in megrok.layout v0.9, megrok.layout.Page is not derived from grok.View, so it does not have the flash. So we will need to brew our own function for that.

We'll put all our message code in a 'message.py' file:

import grok
from zope.interface import Interface
from hurry import yui
from layout import StyleSheets, Scripts

if you're using Grok v1.0, also define this:

from zope import component
from z3c.flashmessage.interfaces import IMessageReceiver, IMessageSource

def flash( message, type='message'):
    source = component.getUtility(IMessageSource, name='session')
    source.send(message, type)

This is the flash function to be used whenever we see fit. It just gets the IMessageSource utility that is registered by Grok, and sends the message to it. You can specify a type to categorize your messages.

class MessagesScript(grok.Viewlet):
    grok.viewletmanager(Scripts)
    grok.context(Interface)
    grok.template('script')

    def update(self):
        yui.json.need()
        yui.container.need()
        yui.dragdrop.need()

    @property
    def messageurl(self):
        return self.view.url(grok.getSite(), 'messages')

class PanelStylesheet(grok.Viewlet):
    grok.viewletmanager(StyleSheets)
    grok.context(Interface)
    template = grok.PageTemplate('<link rel="stylesheet" type="text/css" '
            'tal:attributes="href '
            'context/++resource++yui/container/assets/skins/sam/container.css" />')

Here we loaded the script and tell hurry.yui that it needs to load JSON, Container and DragDrop from the YUI library. DragDrop is needed if we want our messagebox to be movable. There is also a messageurl property method that returns the url to our JSON view which we will define later.

We also loaded the container CSS file provided with YUI.

JSON

These are the contents of the 'script.pt' file (in the 'message_templates' dir):

<div id="messages">
        <div class="hd">Messages</div>
        <div class="bd"></div>
        <div class="ft"></div>
</div>
<tal:tag tal:replace="structure string:<script type='text/javascript'>" />
    messagePanel = new YAHOO.widget.Panel("messages",
                { constraintoviewport:true,
                  visible: false,
                  context: ['body', 'bl', 'bl']
                });
    messagePanel.render();
    function getMessages()
    {
                var callback = {
                  success: function(o) {
                                var messages = YAHOO.lang.JSON.parse(o.responseText);
                                for (i in messages)
                                {
                                        dl = document.createElement('dl');
                                        var msg = messages[i];
                                        dl.innerHTML='<dt>'+ msg.type+'</dt><dd>'+msg.message+'</dd>';
                                        o.argument.appendToBody(dl);
                                        o.argument.show();
                                }
                        },
                  timeout: 3000,
                  argument: this
                };
                YAHOO.util.Connect.asyncRequest('GET',
                                '<tal:tag tal:replace="viewlet/messageurl"/>',
                                callback,
                                null);
    }
    YAHOO.lang.later(5000, messagePanel, getMessages, null, true);
    messagePanel.hideEvent.subscribe(function(type, args, panel)
                    {
                        panel.setBody('')
                    },
                    messagePanel);
<tal:tag tal:replace="structure string:</script>" />

We first defined the HTML code that defines a default YUI Module (of which a Panel is a specilization). We then created a Panel object that refers to that code, and we set some configuration options, amongst which that it should be hidden at the start.

Then we defined the getMessages() function that queries the server for new messages, and if there are, we parse them into a JavaScript object using the YAHOO.lang.JSON.parse function, cast them into HTML and append them to the Panel body and then we show the Panel.

We then wired this function to be called every 5 seconds.

Finally, we attached an event handler to the hideEvent of the Panel, that clears the Panel's body.

Now we are still missing one bit: the JSON view:

class JSONMessages(grok.JSON):
    grok.context(grok.Application)

    def messages(self):
        receiver = component.getUtility(IMessageReceiver)
        return [{'message':m.message, 'type':m.type} for m in receiver.receive()]

A grok.JSON view makes all its (public) methods traversable, and those methods should return something that can be turned into JSON data with the simplejson Python package. In our case, it's an array of dictionaries containing the message data and the type of message.

Messaging

Now we have everything in place to display messages, but we don't send any. Let's edit 'blog.py':

for grok 1.0, add this

from message import flash

In the Add method of the AddForm, add this before the redirect:

self.flash("New blog entry '%s' added!" % entry.title, type='blog')

And in the Save method of the EditForm, add this before the redirect:

self.flash("Blog entry '%s' updated!" % self.context.title, type='blog')

for Grok 1.0, omit the 'self.'. So, whenever we edit or add a blog, a message will be sent.

Restart the server, refresh your page and have a blast!

site_p5.jpg