Skip to content

Commit 0e9f650

Browse files
A couple of small improvements to the custom vue component example (#4229)
Hi there, this is my first PR for this project and I'm somewhat new to collaborative Open Source development, so please be patient. ### Description This PR modifies the Custom Vue Component [example](https://github.com/zauberzeug/nicegui/tree/main/examples/custom_vue_component) to make it a bit more helpful for developers who need to create custom components but haven't used Vue before. ### Motivation In the example linked above, the Vue component is defined in `counter.js`. This way of bundling a component in a vanilla JavaScript file is obviously supported by Vue, since every built-in NiceGUI component is defined this way, but the official Vue documentation (and basically every other resource on the web) assumes that developers are writing `.vue` files, which are supported by additional tooling. I think the example should structure its Vue code in a way that makes it easier for new developers to understand how it works in relation to existing Vue documentation. ### Changes In the new modified example, the python class definition now uses a new file, `counter.vue`, which re-implements `counter.js` in the more customary Vue Single-File Component format. The call to `ui.run()` has been modified to ensure browser reload on changes to the `.js` and `.vue` files, which is important for iterative development on the Vue side. ### Testing These changes do not add any new functionality that needs to be covered by tests. I have run the modified example and ensured that it does not error. ### Additional Notes I am curious if the developers would be interested in adding a second, more advanced custom vue component example. I am in the process of creating a pannable and zoomable image component, which I would be happy to wrap up in a nice example and provide in a separate PR. --------- Co-authored-by: Falko Schindler <[email protected]>
1 parent 1137b50 commit 0e9f650

File tree

5 files changed

+91
-15
lines changed

5 files changed

+91
-15
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Custom Vue Component
2+
3+
This example demonstrates how to create and use custom Vue components in NiceGUI.
4+
One component is implemented using JavaScript, the other using Vue's Single-File Component (SFC) syntax.
5+
6+
## Counter Component (counter.js)
7+
8+
The `Counter` component is a simple counter that increments a value.
9+
On change, it emits an event with the new value.
10+
A reset method allows to reset the counter to 0.
11+
12+
The JavaScript code in `counter.js` defines the front-end logic of the component using JavaScript.
13+
14+
## OnOff Component (on_off.vue)
15+
16+
The `OnOff` component is a simple toggle that switches between on and off.
17+
On change, it emits an event with the new value.
18+
A reset method is also provided to reset the toggle to off.
19+
20+
The Single-File Component in `on_off.vue` defines the front-end logic of the component using Vue 2.
21+
In contrast to the JavaScript code in `counter.js`, it splits the template, script, and style into separate sections.

examples/custom_vue_component/counter.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
// NOTE: Make sure to reload the browser with cache disabled after making changes to this file.
22
export default {
33
template: `
4-
<button @click="handle_click">
5-
<strong>{{title}}: {{value}}</strong>
6-
</button>`,
4+
<button @click="handle_click" :style="{ background: value > 0 ? '#bf8' : '#eee', padding: '8px 16px', borderRadius: '4px' }">
5+
<strong>{{title}}: {{value}}</strong>
6+
</button>
7+
`,
8+
props: {
9+
title: String,
10+
},
711
data() {
812
return {
913
value: 0,
@@ -18,7 +22,4 @@ export default {
1822
this.value = 0;
1923
},
2024
},
21-
props: {
22-
title: String,
23-
},
2425
};

examples/custom_vue_component/main.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
#!/usr/bin/env python3
22
from counter import Counter
3+
from on_off import OnOff
34

45
from nicegui import ui
56

6-
ui.markdown('''
7-
#### Try the new click counter!
7+
with ui.row(align_items='center'):
8+
counter = Counter('Count', on_change=lambda e: ui.notify(f'The value changed to {e.args}.'))
9+
ui.button('Reset', on_click=counter.reset).props('outline')
810

9-
Click to increment its value.
10-
''')
11-
with ui.card():
12-
counter = Counter('Clicks', on_change=lambda e: ui.notify(f'The value changed to {e.args}.'))
11+
with ui.row(align_items='center'):
12+
on_off = OnOff('State', on_change=lambda e: ui.notify(f'The value changed to {e.args}.'))
13+
ui.button('Reset', on_click=on_off.reset).props('outline')
1314

1415

15-
ui.button('Reset', on_click=counter.reset).props('small outline')
16-
17-
ui.run()
16+
ui.run(uvicorn_reload_includes='*.py,*.js,*.vue')
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from typing import Callable, Optional
2+
3+
from nicegui.element import Element
4+
5+
6+
class OnOff(Element, component='on_off.vue'):
7+
8+
def __init__(self, title: str, *, on_change: Optional[Callable] = None) -> None:
9+
super().__init__()
10+
self._props['title'] = title
11+
self.on('change', on_change)
12+
13+
def reset(self) -> None:
14+
self.run_method('reset')
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<template>
2+
<div>
3+
<button @click="handle_click" :class="{ active: value }">
4+
<strong>{{ title }}: {{ value ? "ON" : "OFF" }}</strong>
5+
</button>
6+
</div>
7+
</template>
8+
9+
<script>
10+
export default {
11+
props: {
12+
title: String,
13+
},
14+
data() {
15+
return {
16+
value: false,
17+
};
18+
},
19+
methods: {
20+
handle_click() {
21+
this.value = !this.value;
22+
this.$emit("change", this.value);
23+
},
24+
reset() {
25+
this.value = false;
26+
},
27+
},
28+
};
29+
</script>
30+
31+
<style scoped>
32+
button {
33+
background-color: #eee;
34+
padding: 8px 16px;
35+
border-radius: 4px;
36+
}
37+
38+
button.active {
39+
background-color: #fb8;
40+
}
41+
</style>

0 commit comments

Comments
 (0)