Procházet zdrojové kódy

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 <falko@zauberzeug.com>
Joseph Burkhart před 3 měsíci
rodič
revize
0e9f650e69

+ 21 - 0
examples/custom_vue_component/README.md

@@ -0,0 +1,21 @@
+# Custom Vue Component
+
+This example demonstrates how to create and use custom Vue components in NiceGUI.
+One component is implemented using JavaScript, the other using Vue's Single-File Component (SFC) syntax.
+
+## Counter Component (counter.js)
+
+The `Counter` component is a simple counter that increments a value.
+On change, it emits an event with the new value.
+A reset method allows to reset the counter to 0.
+
+The JavaScript code in `counter.js` defines the front-end logic of the component using JavaScript.
+
+## OnOff Component (on_off.vue)
+
+The `OnOff` component is a simple toggle that switches between on and off.
+On change, it emits an event with the new value.
+A reset method is also provided to reset the toggle to off.
+
+The Single-File Component in `on_off.vue` defines the front-end logic of the component using Vue 2.
+In contrast to the JavaScript code in `counter.js`, it splits the template, script, and style into separate sections.

+ 7 - 6
examples/custom_vue_component/counter.js

@@ -1,9 +1,13 @@
 // NOTE: Make sure to reload the browser with cache disabled after making changes to this file.
 export default {
   template: `
-  <button @click="handle_click">
-    <strong>{{title}}: {{value}}</strong>
-  </button>`,
+    <button @click="handle_click" :style="{ background: value > 0 ? '#bf8' : '#eee', padding: '8px 16px', borderRadius: '4px' }">
+      <strong>{{title}}: {{value}}</strong>
+    </button>
+  `,
+  props: {
+    title: String,
+  },
   data() {
     return {
       value: 0,
@@ -18,7 +22,4 @@ export default {
       this.value = 0;
     },
   },
-  props: {
-    title: String,
-  },
 };

+ 8 - 9
examples/custom_vue_component/main.py

@@ -1,17 +1,16 @@
 #!/usr/bin/env python3
 from counter import Counter
+from on_off import OnOff
 
 from nicegui import ui
 
-ui.markdown('''
-#### Try the new click counter!
+with ui.row(align_items='center'):
+    counter = Counter('Count', on_change=lambda e: ui.notify(f'The value changed to {e.args}.'))
+    ui.button('Reset', on_click=counter.reset).props('outline')
 
-Click to increment its value.
-''')
-with ui.card():
-    counter = Counter('Clicks', on_change=lambda e: ui.notify(f'The value changed to {e.args}.'))
+with ui.row(align_items='center'):
+    on_off = OnOff('State', on_change=lambda e: ui.notify(f'The value changed to {e.args}.'))
+    ui.button('Reset', on_click=on_off.reset).props('outline')
 
 
-ui.button('Reset', on_click=counter.reset).props('small outline')
-
-ui.run()
+ui.run(uvicorn_reload_includes='*.py,*.js,*.vue')

+ 14 - 0
examples/custom_vue_component/on_off.py

@@ -0,0 +1,14 @@
+from typing import Callable, Optional
+
+from nicegui.element import Element
+
+
+class OnOff(Element, component='on_off.vue'):
+
+    def __init__(self, title: str, *, on_change: Optional[Callable] = None) -> None:
+        super().__init__()
+        self._props['title'] = title
+        self.on('change', on_change)
+
+    def reset(self) -> None:
+        self.run_method('reset')

+ 41 - 0
examples/custom_vue_component/on_off.vue

@@ -0,0 +1,41 @@
+<template>
+  <div>
+    <button @click="handle_click" :class="{ active: value }">
+      <strong>{{ title }}: {{ value ? "ON" : "OFF" }}</strong>
+    </button>
+  </div>
+</template>
+
+<script>
+export default {
+  props: {
+    title: String,
+  },
+  data() {
+    return {
+      value: false,
+    };
+  },
+  methods: {
+    handle_click() {
+      this.value = !this.value;
+      this.$emit("change", this.value);
+    },
+    reset() {
+      this.value = false;
+    },
+  },
+};
+</script>
+
+<style scoped>
+button {
+  background-color: #eee;
+  padding: 8px 16px;
+  border-radius: 4px;
+}
+
+button.active {
+  background-color: #fb8;
+}
+</style>