Providing values to the arguments of an Event on the fly

As explained in the tutorial example, when transitioning to an UrlBoundView which is parameterised, the Event used to specify the transition has arguments matching those of the destination UrlBoundView and also values for these arguments.

Event argument values are supplied where the Event is passed to a ButtonInput by calling the with_arguments() method.

Sometimes, however, those argument values are not yet known at this time:

In this example an Address is added on the ‘/add’ view, and the user is transitioned after that to the ‘/review’ view. The ‘/review’ view needs an argument: the id of the added address to review.

When the ButtonInput is rendered on the ‘/add’ view, the new Address is not yet saved to the database, and thus its id is not known. Only once the user clicks on ‘Save’ do we know the id of the new Address.

In this case, instead of passing the ButtonInput an Event with_arguments(), pass an Event with_returned_argument() :

class AddressForm(Form):
    def __init__(self, view):
        super().__init__(view, 'address_form')

        new_address = Address()
        grouped_inputs = self.add_child(FieldSet(view, legend_text='Add an address'))
        grouped_inputs.use_layout(FormLayout())
        grouped_inputs.layout.add_input(TextInput(self, new_address.fields.name))
        grouped_inputs.layout.add_input(TextInput(self, new_address.fields.email_address))

        grouped_inputs.add_child(Button(self, new_address.events.save.with_returned_argument('address_id'), style='primary'))

Declare the save Event with an address_id argument and return the new Address’ id from the method executed by the Action of the save Event:

class Address(Base):
    __tablename__ = 'eventresult_address'
    
    id            = Column(Integer, primary_key=True)
    email_address = Column(UnicodeText)
    name          = Column(UnicodeText)
    reviewed      = Column(Boolean)

    fields = ExposedNames()
    fields.name = lambda i: Field(label='Name', required=True)
    fields.email_address = lambda i: EmailField(label='Email', required=True)

    def save(self):
        Session.add(self)
        Session.flush()
        return self.id
        
    def review(self):
        self.reviewed = True

    events = ExposedNames()
    events.save = lambda i: Event(label='Save', action=Action(i.save), address_id=IntegerField())
    events.review = lambda i: Event(label='Mark as reviewed', action=Action(i.review))

Also parameterise the ‘/review’ UrlBoundView to require a matching address_id argument:

class AddressBookUI(UserInterface):
    def assemble(self):
        home = self.define_view('/', title='Show')
        add = self.define_view('/add', title='Add')
        review = self.define_view('/review', view_class=ReviewView, 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, review)
        self.define_transition(Address.events.review, review, home)