Buttons allow users to act¶
All that’s left to complete our application is to provide a means for the user to instruct the application to perform some task. In this case, to save the new address to the database.
Augmenting a model with Events¶
Just like a Field
is used to
expose an item of data inside your model as part of a user interface,
an Event
is something that can be
triggered by a user in order to execute a method on the model object.
To do this, use the @exposed decorator as before, this time to
define the events on your object. An
Event
can optionally be
passed an Action
. This is
just a wrapper around any callable – the callable that will get called if
a user triggers this Event
:
class Address(Base):
__tablename__ = 'addressbook2_address'
id = Column(Integer, primary_key=True)
email_address = Column(UnicodeText)
name = Column(UnicodeText)
@exposed
def fields(self, fields):
fields.name = Field(label='Name', required=True)
fields.email_address = EmailField(label='Email', required=True)
def save(self):
Session.add(self)
@exposed
def events(self, events):
events.save = Event(label='Save', action=Action(self.save))
Adding the Save Button¶
The last missing piece of the address book application is the “Save”
Button
. It is child’s play to add it – you
merely add a Button
Widget
as well, passing it the
Event
defined on your model
object.
But there’s a catch: your application won’t know how to handle this
Event
if you do not first
define an EventHandler
for that
Event
. Luckily in this simple
scenario that is easy:
class AddAddressForm(Form):
def __init__(self, view):
super(AddAddressForm, self).__init__(view, 'add_form')
new_address = Address()
grouped_inputs = self.add_child(InputGroup(view, label_text='Add an address'))
grouped_inputs.add_child( LabelledBlockInput(TextInput(self, new_address.fields.name)) )
grouped_inputs.add_child( LabelledBlockInput(TextInput(self, new_address.fields.email_address)) )
self.define_event_handler(new_address.events.save)
grouped_inputs.add_child(Button(self, new_address.events.save))
Try it out¶
We’re done with our first simple application. Do try it out and discover how it behaves: its validation, for example, and how it behaves when JavaScript is not active. Just remember all the housekeeping tasks you need to do before running the application!
Here is the complete Python source of our example application:
from __future__ import print_function, unicode_literals, absolute_import, division
from sqlalchemy import Column, Integer, UnicodeText
from reahl.sqlalchemysupport import Session, Base
from reahl.web.fw import UserInterface, Widget
from reahl.web.ui import HTML5Page, Form, TextInput, LabelledBlockInput, Button, Panel, P, H, InputGroup
from reahl.component.modelinterface import exposed, EmailField, Field, Event, Action
class AddressBookUI(UserInterface):
def assemble(self):
self.define_view('/', title='Addresses', page=AddressBookPage.factory())
class AddressBookPage(HTML5Page):
def __init__(self, view):
super(AddressBookPage, self).__init__(view, style='basic')
self.body.add_child(AddressBookPanel(view))
class AddressBookPanel(Panel):
def __init__(self, view):
super(AddressBookPanel, self).__init__(view)
self.add_child(H(view, 1, text='Addresses'))
for address in Session.query(Address).all():
self.add_child(AddressBox(view, address))
self.add_child(AddAddressForm(view))
class AddAddressForm(Form):
def __init__(self, view):
super(AddAddressForm, self).__init__(view, 'add_form')
new_address = Address()
grouped_inputs = self.add_child(InputGroup(view, label_text='Add an address'))
grouped_inputs.add_child( LabelledBlockInput(TextInput(self, new_address.fields.name)) )
grouped_inputs.add_child( LabelledBlockInput(TextInput(self, new_address.fields.email_address)) )
self.define_event_handler(new_address.events.save)
grouped_inputs.add_child(Button(self, new_address.events.save))
class AddressBox(Widget):
def __init__(self, view, address):
super(AddressBox, self).__init__(view)
self.add_child(P(view, text='%s: %s' % (address.name, address.email_address)))
class Address(Base):
__tablename__ = 'addressbook2_address'
id = Column(Integer, primary_key=True)
email_address = Column(UnicodeText)
name = Column(UnicodeText)
@exposed
def fields(self, fields):
fields.name = Field(label='Name', required=True)
fields.email_address = EmailField(label='Email', required=True)
def save(self):
Session.add(self)
@exposed
def events(self, events):
events.save = Event(label='Save', action=Action(self.save))