Reacting to user events

In Reahl, each screen the user can see is called a View. The user is transitioned from one View to another depending on which Event a user action triggers.

You program how the user is transitioned between different Views depending on which Events are fired, and you can make such transitions conditional using Guards.

The example here has three Views. If the user submits a comment on the home View, and did enter some text for the comment, the next View shown is ‘/thanks’. If the user did not enter any text, ‘/none’ is shown instead:

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

Events are defined on an object (just like Fields are). Each Button is linked to the Event it will fire when clicked. Each Event is in turn optionally linked to an Action.

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

from __future__ import print_function, unicode_literals, absolute_import, division

from reahl.web.fw import UserInterface
from reahl.web.ui import Button, Form, LabelledBlockInput, P, TextInput, HTML5Page
from reahl.web.layout import PageLayout
from reahl.web.pure import ColumnLayout, UnitSize
from reahl.component.modelinterface import exposed, EmailField, Field
from reahl.component.modelinterface import Event, Action, Not

class PageFlowUI(UserInterface):
    def assemble(self):
        contents_layout = ColumnLayout(('main', UnitSize('1/2'))).with_slots()
        page_layout = PageLayout(contents_layout=contents_layout)
        self.define_page(HTML5Page, style='basic').use_layout(page_layout)  

        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_text = 'Thanks for submitting your comment'
        thanks.set_slot('main', P.factory(text=thanks_text))

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

        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')

        email_input = TextInput(self, comment.fields.email_address)
        self.add_child(LabelledBlockInput(email_input))

        text_input = TextInput(self, comment.fields.text)
        self.add_child(LabelledBlockInput(text_input))

        self.add_child(Button(self, comment.events.submit))



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.

Previous topic

Layout example

Next topic

Persistence