Theme
Version
GitHub PyPI Discord
On this page

Add browser behavior

Some interactions should respond at once, without asking the server: a counter, a toggle, a menu that opens. So far everything you built finishes in Python. In this step you add behavior that runs in the browser, with no request and no page reload.

Citry uses Vue for browser behavior. You write Vue attributes in the template, and Python gives each component its starting browser data.

Build the counters

Save this example as click_counters.py:

Independent click counters
from citry import Component

class ClickCounter(Component):
    class Kwargs:
        name: str

    class Slots:
        pass

    def js_data(self, kwargs: Kwargs, slots: Slots):
        return {"name": kwargs.name, "count": 0}

    template = """
      <button
        class="counter"
        type="button"
        @click="count = count + 1"
      >
        <span class="counter__name" v-text="name"></span>
        clicked
        <span class="counter__count" v-text="count"></span>
        times
      </button>
    """

class CounterPage(Component):
    class Kwargs:
        pass

    class Slots:
        pass

    template = """
      <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="utf-8" />
          <title>Component data</title>
        </head>
        <body>
          <c-ClickCounter name="Ada" />
          <c-ClickCounter name="Grace" />
        </body>
      </html>
    """

page = CounterPage()

if __name__ == "__main__":
    print(page)

page

Create the page and open it in a browser:

python click_counters.py > click_counters.html

Both buttons start at zero. Click Ada's button: Ada changes to one, and Grace stays at zero.

Update on click

Two Vue attributes, called directives, connect the button to its data:

When Citry sees Vue directives in a template, it adds its browser code to the page. You do not need a separate JavaScript entry file or Vue setup.

Set starting data

js_data() returns the data the component starts with in the browser:

def js_data(self, kwargs: Kwargs, slots: Slots):
    return {"name": kwargs.name, "count": 0}

The values must be JSON-serializable. Vue watches each top-level key, and when one changes, it updates the parts of the page that show it. Each counter gets its own copy, so clicking Ada cannot change Grace.

Add Vue options

When a component needs more than data, such as methods, props, emitted events, or code that runs when it first appears on the page, pass Vue options to $component in the component's js string. The js string holds the component's JavaScript, the way css holds its CSS:

$component({
  methods: {
    reset() {
      this.count = 0;
    },
  },
});

The next step uses this to connect two components. Component options documents everything $component accepts, and Vue in templates covers the template syntax.

Next steps

Next, connect a parent and child component in the browser.