Reacting to user events

Any web application needs to control what gets executed in response to a user action, and also how the user is transitioned between the different pages of an application. The application presented here illustrates the basics of how this is done in Reahl.

There are three pages in the example application presented here. If the user submits a comment on the home page, and did enter some text for the comment, the next page shown is ‘/thanks’. If the user did not enter any text, ‘/none’ is shown instead.

A visual representation shows this succinctly:

A graph showing each View as a node, with arrows showing how the user would transition between the views.

Remember how user input is controlled by means of Fields? The Actions a user can trigger are similarly controlled by means of Events.

Note in the code below how a Comment @exposes a submit Event, which will result in the execution of the submit method of the Comment when triggered. The execution of the submit method renders the following output on the console:

[email protected] submitted a comment:
some comment text

To enable a user to actually trigger this Event, a Button is linked to the Event.

Defining the entire design depicted visually above is done in one place: the assemble method. Here each View is defined, as well as a number of Transitions between Views.

Note

This example does not set a different page for each View. That would have required us to create a page class for each View. We rather use a different technique using Slots and a page defined for the entire UserInterface that saves some typing.

from __future__ import unicode_literals
from __future__ import print_function
from reahl.web.fw import UserInterface
from reahl.web.ui import Button
from reahl.web.ui import Form
from reahl.web.ui import LabelledBlockInput
from reahl.web.ui import P
from reahl.web.ui import TextInput
from reahl.web.ui import TwoColumnPage
from reahl.component.modelinterface import exposed, EmailField, Field, Event, Action, Not

class PageFlowUI(UserInterface):
    def assemble(self):
        self.define_page(TwoColumnPage, style='basic')  

        comment = Comment()

        home = self.define_view('/', title='Page flow demo')
        home.set_slot('main', CommentForm.factory(comment))

        thanks = self.define_view('/thanks', title='Thank you!')
        thanks.set_slot('main', P.factory(text='Thanks for submitting your comment'))

        none_submitted = self.define_view('/none', title='Nothing to say?')
        none_submitted.set_slot('main', P.factory(text='Mmm, you submitted an empty comment??'))

        self.define_transition(comment.events.submit, home, thanks, guard=Action(comment.contains_text))
        self.define_transition(comment.events.submit, home, none_submitted, guard=Not(Action(comment.contains_text)))

        
class Comment(object):
    @exposed
    def fields(self, fields):
        fields.email_address = EmailField(label='Email address', required=True)
        fields.text = Field(label='Comment')

    @exposed
    def events(self, events):
        events.submit = Event(label='Submit', action=Action(self.submit))

    def submit(self):
        print('%s submitted a comment:' % self.email_address)
        print(self.text)

    def contains_text(self):
        return self.text and self.text.strip() != ''


class CommentForm(Form):
    def __init__(self, view, comment):
        super(CommentForm, self).__init__(view, 'myform')

        self.add_child( LabelledBlockInput(TextInput(self, comment.fields.email_address)) )
        self.add_child( LabelledBlockInput(TextInput(self, comment.fields.text)) )
        self.add_child( Button(self, comment.events.submit) )

Previous topic

Layout example

Next topic

Persistence