Connect components in the browser
Often a parent component holds a value and a child component shows it or asks to change it, all in the browser: a picker and its button, a list and its filter. In Vue, the parent passes the value down as a prop, and the child sends a named event up when something happens. The child does not decide what the event does; the parent does.
In this step you build a choice button whose label comes from its parent. Clicking the button tells the parent, the parent changes its choice, and the new label shows in the button.
Build parent and child
Save this as connected_components.py:
from citry import Component
class ChoiceButton(Component):
class Kwargs:
pass
class Slots:
pass
template = """
<button
class="choice-button"
type="button"
@click="$emit('select')"
>
Choose
<span
class="choice-button__label"
v-text="label"
></span>
</button>
"""
js = """
$component({
props: {
label: { type: String, required: true },
},
emits: ['select'],
});
"""
class ChoicePicker(Component):
class Kwargs:
pass
class Slots:
pass
template = """
<section class="choice-picker">
<p>
Current choice:
<output
class="choice-picker__value"
v-text="choice"
></output>
</p>
<c-ChoiceButton
:label="choice"
@select="toggleChoice"
/>
</section>
"""
js = """
$component({
data() {
return { choice: 'Ocean' };
},
methods: {
toggleChoice() {
this.choice =
this.choice === 'Ocean' ? 'Forest' : 'Ocean';
},
},
});
"""
class ChoicePage(Component):
class Kwargs:
pass
class Slots:
pass
template = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Connect components</title>
</head>
<body>
<c-ChoicePicker />
</body>
</html>
"""
page = ChoicePage()
if __name__ == "__main__":
print(page)
page
python connected_components.py > connected_components.html
The picker starts at βOcean.β Each click switches between βForestβ and βOcean.β
Declare props and events
ChoiceButton declares a label prop and a select event in its Vue options:
$component({
props: {
label: { type: String, required: true },
},
emits: ['select'],
});
The template shows label with v-text, and the button sends select with $emit when clicked:
<button type="button" @click="$emit('select')">
Choose <span v-text="label"></span>
</button>
Props are not the same as Kwargs. Python reads Kwargs once, when it renders the HTML. A prop lives in the browser and can change without another Python render. Vue's guides cover props and component events in more depth.
Connect parent to child
ChoicePicker uses the button like this:
<c-ChoiceButton :label="choice" @select="toggleChoice" />
:label passes the parent's choice to the child, and keeps it up to date when choice changes. @select calls the parent's toggleChoice method when the child sends select. The parent holds the value and decides what to do:
$component({
data() {
return { choice: 'Ocean' };
},
methods: {
toggleChoice() {
this.choice =
this.choice === 'Ocean' ? 'Forest' : 'Ocean';
},
},
});
data() gives the parent its starting browser data from JavaScript, as js_data() does from Python.
The Vue section of the docs covers more, for example:
- Content and slots for slots in the browser;
v-showneeds one root for components with several root elements;onServerRendercallback for code that runs when a component first appears on the page.
Next steps
Next, serve the page with FastAPI so a later click can reach Python.