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:
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:
firstname.lastname@example.org 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.
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 print_function, unicode_literals, absolute_import, division 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) )