Keep component properties in sync

Use bind() to update an Anvil component property from reactive state. Use writeback() when changes made in the component should also update that state.

When an app uses Anvil Reactive for state, prefer these functions for new component bindings. They update from the same state as your computed values and effects, and they stop and start with the component's page lifetime.

Existing native Anvil Data Bindings remain compatible and do not need to be migrated just because Reactive is installed.

Update a component from state

Call bind() after the Form has created its components:

from anvil_reactive.main import bind


def __init__(self, **properties):
    super().__init__(**properties)

    bind(
        self.count_label,
        "text",
        lambda: f"Count: {self.state.count}",
    )

You can also bind directly to an object attribute or a dictionary key:

bind(self.name_label, "text", self.state, "name")
bind(self.status_label, "text", self.state_dict, "status")

bind() sets the component property immediately. Later updates run while the component is on the page, stop when it is removed, and catch up with the latest state if it is added again.

Set up each binding once, normally in the Form's __init__. Calling bind() or writeback() again installs another binding; it does not replace the earlier one. In particular, do not call either function from a property setter that may run more than once.

Update state from a component

writeback() includes the one-way behavior of bind() and writes the component property back when one of the chosen events fires:

from anvil_reactive.main import writeback

writeback(
    self.name_box,
    "text",
    self.state,
    "name",
    "change",
)

After the TextBox raises its change event, self.state.name contains the new text. A change handler added afterward can read the updated state.

Use a getter and setter when the value needs conversion or validation. You can also listen for more than one event:

writeback(
    self.name_box,
    "text",
    lambda: self.state.name,
    lambda value: setattr(self.state, "name", value.strip()),
    ("change", "pressed_enter"),
)

For an Anvil TextBox, change writes as the user edits the text. Use lost_focus or pressed_enter if you want to write when the user leaves the field or presses Enter.

Edit a buffered Model Class

A reactive, buffered Data Table Model Class can be used directly as a Form's editing state. This example writes the TextBox value to the model on change and enables Save while the model has buffered changes:

Create a BookForm with a TextBox named title_box and Buttons named save_button and cancel_button. Pass it a row from the reactive Book model.

from anvil import handle
from anvil_reactive.main import bind, writeback

from ._anvil_designer import BookFormTemplate


class BookForm(BookFormTemplate):
    def __init__(self, book, **properties):
        super().__init__(**properties)
        self.book = book

        writeback(self.title_box, "text", self.book, "title", "change")
        bind(
            self.save_button,
            "enabled",
            lambda: bool(self.book.buffered_changes),
        )

    @handle("save_button", "click")
    def save_button_click(self, **event_args):
        self.book.save()

    @handle("cancel_button", "click")
    def cancel_button_click(self, **event_args):
        self.book.reset()

writeback() also performs the initial state-to-component update. Calling reset() restores both the buffered model and the bound components. The Model Class must be made reactive for later changes to update the Form.