Parameterised Views¶
The AddressBook example can be changed to allow editing of existing Addresses.
Views for editing addresses.
A View can have arguments—so that a single “Edit” View is
defined for an as yet unknown Address. Computing the actual
contents of the View is delayed until the Address argument becomes
available.
How to create a parameterised view¶
To specify that a View has arguments, create your own class that
inherits from UrlBoundView. Give it an
assemble() method with keyword
arguments that represent the arguments to the
UrlBoundView.
Customise the View based on the arguments given inside this
assemble() method. The title of the
UrlBoundView is set by setting
title. Populate the Slots by
calling set_slot()
class EditView(UrlBoundView):
def assemble(self, address_id=None):
try:
address = Session.query(Address).filter_by(id=address_id).one()
except NoResultFound:
raise CannotCreate()
self.title = 'Edit %s' % address.name
self.set_slot('main', EditAddressForm.factory(address))
If an EditView is requested for an address_id that does not exist, raise
a CannotCreate to indicate that the
EditView does not exist for the given arguments.
How to define a parameterised view¶
To define a parameterised View, use the view_class keyword
argument to define_view().
The framework parses arguments from the URL of the UrlBoundView and
passes these into the call to assemble().
Fields describe how the framework manages arguments sent to the
View via its URL. Each Field sent as a keyword argument to
define_view() is used to compute
the value of a matching keyword argument in assemble().
class AddressBookUI(UserInterface):
def assemble(self):
home = self.define_view('/', title='Show')
add = self.define_view('/add', title='Add')
edit = self.define_view('/edit', view_class=EditView, address_id=IntegerField())
home.set_slot('main', AddressBookPanel.factory(self))
add.set_slot('main', AddressForm.factory())
bookmarks = [f.as_bookmark(self) for f in [home, add]]
self.define_page(AddressBookPage, bookmarks)
self.define_transition(Address.events.save, add, home)
self.define_transition(Address.events.update, edit, home)
self.edit = edit
def get_edit_bookmark(self, address, description=None):
return self.edit.as_bookmark(self, address_id=address.id, description=description)
Bookmarks to parameterised Views¶
In any Reahl application there are two ways to get to a UrlBoundView:
- click on an
Acreated from aBookmark - the application automatically switches to the
UrlBoundViewbecause of a transition.
Our ‘tutorial.parameterised1’ example creates an A next to each
listed address:
class AddressBox(Widget):
def __init__(self, view, address, address_book_ui):
super(AddressBox, self).__init__(view)
bookmark = address_book_ui.get_edit_bookmark(address=address, description='edit')
paragraph = self.add_child(P(view, text='%s: %s ' % (address.name, address.email_address)))
paragraph.add_child(A.from_bookmark(view, bookmark))
A distinct Bookmark is computed for each Address. This is done in
AddressbookUI.get_edit_bookmark:
def get_edit_bookmark(self, address, description=None):
return self.edit.as_bookmark(self, address_id=address.id, description=description)
Transitions to parameterised Views¶
The ‘tutorial.parameterised2’ example shows how you would
automatically transition a user to a parameterised view. The example
has an edit Button instead of a link placed next to each
Address. First, define a transition that will fire when the “edit”
Button gets clicked:
class AddressBookUI(UserInterface):
def assemble(self):
home = self.define_view('/', title='Show')
add = self.define_view('/add', title='Add')
edit = self.define_view('/edit', view_class=EditView, address_id=IntegerField())
home.set_slot('main', AddressBookPanel.factory())
add.set_slot('main', AddressForm.factory())
bookmarks = [f.as_bookmark(self) for f in [home, add]]
self.define_page(AddressBookPage, bookmarks)
self.define_transition(Address.events.save, add, home)
self.define_transition(Address.events.update, edit, home)
self.define_transition(Address.events.edit, home, edit)
A Button has to be in a Form, so AddressBox must change to be a
Form. Call
with_arguments() on the
Event to which the Button is tied so that the user will transition
to an EditView matching that specific Address.
class AddressBox(Form):
def __init__(self, view, address):
form_name = 'address_%s' % address.id # Forms need unique names!
super(AddressBox, self).__init__(view, form_name)
paragraph = self.add_child(P(view, text='%s: %s ' % (address.name, address.email_address)))
paragraph.add_child(Button(self, address.events.edit.with_arguments(address_id=address.id)))
Programmatic arguments¶
Not all the arguments passed to the .assemble() method of a View
need to be parsed from the URL of the View. Sometimes it is useful to
pass an object that is available in the .assemble() of the
containing UserInterface to the .assemble() of one of its Views .
For example, the .assemble() of a particular View may need access to a
Bookmark which is computed inside the .assemble() of its UserInterface.
A View can be parameterised by such arguments as well. Just pass the
actual value as keyword argument to .define_view(). The framework
distinguishes between normal programmatic arguments and those that
have to be parsed from the URL based on the fact that Field instances
are sent for the arguments that need to be parsed from the URL. At the
end of the day they’re all just arguments to the View though.
Multiple possible destinations¶
In cases where there are multiple Transitions possible, things get
tricky:
At the time of defining the Event or placing the Button the exact
target View that will be transitioned to is not known yet. The
target View transitioned to will depend on the Transition
chosen – something only known when the Event occurs. So be sure to
specify all possible arguments to all possible target Views of all
possible Transitions from the View on which the Button is placed!
