Vue in templates
Some parts of a page should react the moment the user acts: a click opens a panel, typing updates a preview, a button counts up. Waiting for the server to render again would be slow and unnecessary. For these, write Vue attributes in the component's template. They run in the browser.
Citry adds Vue to the page as soon as one component on it uses Vue. You write Vue's own attributes, such as @click and v-show, on the HTML in the template, and define the component's browser data with $component({...}) in the component's js.
Add a browser counter
In the component's js, call $component({...}) and return the values the browser keeps from a data() function, as in any Vue component. The template holds the Vue attributes and can read and change count directly:
from citry import Component
class Counter(Component):
template = """
<button type="button" @click="count += 1">
Add one
</button>
<output v-text="count"></output>
"""
js = """
$component({
data() {
return { count: 0 };
},
});
"""
Each copy of the component on the page keeps its own count.
To start count from a Python value instead, return it from js_data(). Vue in Citry explains what runs on the server and what runs in the browser.
Vue v-* directives
Vue attributes that start with v- are called directives. Vue also has short forms for listening to events and setting attributes:
| Form | What it does |
|---|---|
v-text="label" | Sets an element's text. |
v-show="open" | Shows the element while the expression is true. |
v-if="ready" | Adds or removes the element. |
v-for="item in items" | Repeats the element in the browser. |
v-model="query" | Keeps a form field and a value in step. |
@click="open = true" | Short for v-on:click="open = true". |
:disabled="busy" | Short for v-bind:disabled="busy". |
Modifiers stay in the attribute name. @keydown.enter.prevent="submit()" listens for Enter and stops the browser's default action.
See Vue's template syntax guide for every directive and modifier.
Citry rejects key names on non-keyboard events
Citry accepts a key name such as .enter or .escape only on keydown, keyup, and keypress. On any other event the template fails when it loads. Other events, such as click, have no key, so Vue would ignore @click.enter and run the listener on every click. The same applies to a @c-* binding such as @c-click.enter. To react to a key, listen to keydown or keyup, as in @keydown.enter. System keys such as .ctrl and mouse buttons such as .left still work on click.
Write directive names in lowercase
Write the v- prefix and Vue's own directive names in lowercase. V-IF and v-If fail when the template loads, on HTML elements and component tags alike, because Vue would read V-IF as a plain attribute and v-If as a custom directive named If. Names that start with v-c- or v-citry- fail too, because Citry keeps them for its own use.
Choose v-for or <c-for>
Use v-for and v-if for HTML that the browser adds, removes, or repeats inside one component:
<ul>
<li v-for="item in items" :key="item.id" v-text="item.label"></li>
</ul>
Use <c-for> and <c-if> when the loop or condition creates Citry components. Only Python creates Citry components, so a v-for cannot repeat one; see v-for on components.
c-* with : bindings
:class adds to an element's classes rather than replacing them. Vue joins it with class, and Citry joins it with c-class the same way:
<article
class="card"
c-class="{'card--done': task.completed}"
:class="{ 'card--dragging': dragging }"
>
...
</article>
The card starts as card card--done, and Vue adds card--dragging while dragging is true. :style works the same way with style and c-style. Vue applies :style last, so it wins when both set the same property.
Every other attribute must be set in one place. Setting it from Python and from Vue, such as c-title with :title, fails when the template loads:
<!-- Fails: Python and Vue both set `title`. -->
<a c-title="label" :title="hint">...</a>
Keep only c-title when Python decides the value. Keep only :title when the browser should change it later, and send the starting value with js_data().
Citry rejects Vue code from c-bind
Citry rejects a Vue directive, : binding, or @ listener, or any name that starts with ., ^, or #, that comes from c-bind or is written with a c- prefix, such as c-v-if. Rendering the template raises citry.VueCodeError, on every page, even one that does not use Vue. Write the directive directly in the template, as in v-if="open". Security and CSP explains why.
Vue on component tags
A Citry component tag, such as <c-StatusBadge>, accepts Vue props, listeners, v-if, v-model, and v-show. Props and events lists them and shows how a parent and child pass data and events. To pass content into a named slot, use <c-fill name="...">, not Vue's <template #name>; see <template #name> slots.
More about Vue in Citry
- Vue in Citry explains how Citry runs Vue and which Vue version it ships.
- Component options covers
$component({...}):data(),methods,js_data(), lifecycle hooks, andsetup(). - Content and slots shows which Vue data slot content reads.
- Plugins and the Vue app adds directives and components to every Vue app on the page.
- Limits and errors lists what Citry does not support and how to fix common template errors.