Open StarDustEins opened 1 month ago
Thanks for reporting this issue, @StarDustEins!
Minimum reproducible example:
with ui.stepper().props('vertical animated') as stepper:
with ui.step('A'):
with ui.stepper_navigation():
ui.button('Next', on_click=stepper.next)
with ui.step('B'):
with ui.stepper_navigation():
ui.button('Next', on_click=stepper.next)
ui.button('Back', on_click=stepper.previous)
with ui.step('C'):
with ui.stepper_navigation():
ui.button('Back', on_click=stepper.previous)
So far I failed to reproduce it with plain Quasar/Vue:
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Roboto:400|Material+Icons" rel="stylesheet" type="text/css" />
<link href="https://cdn.jsdelivr.net/npm/quasar@2.17.0/dist/quasar.prod.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="q-app">
<q-stepper ref="stepper" :model-value="step" @update:model-value="onStepChange" vertical animated>
<q-step :name="1" title="Step 1">
<q-stepper-navigation>
<q-btn @click="next" label="Next"></q-btn>
</q-stepper-navigation>
</q-step>
<q-step :name="2" title="Step 2">
<q-stepper-navigation>
<q-btn @click="next" label="Next"></q-btn>
<q-btn @click="previous" label="Previous"></q-btn>
</q-stepper-navigation>
</q-step>
<q-step :name="3" title="Step 3">
<q-stepper-navigation>
<q-btn @click="previous" label="Previous"></q-btn>
</q-stepper-navigation>
</q-step>
</q-stepper>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
<script src="https://cdn.jsdelivr.net/npm/quasar@2.17.0/dist/quasar.umd.prod.js"></script>
<script>
const app = Vue.createApp({
setup() {
const step = Vue.ref(1);
const stepper = Vue.ref(null);
return {
step,
stepper,
onStepChange: (newStep) => (step.value = newStep),
next: () => stepper.value.next(),
previous: () => stepper.value.previous(),
};
},
});
app.use(Quasar);
app.mount("#q-app");
</script>
</body>
</html>
This animates as expected. But what's the difference to a NiceGUI app? Does anyone have an idea?
I have no idea about Vue and js
@falkoschindler I suspect the reason for the issue is that the stepper rebuilds all its child elements every time it updates
I looked into this issue once again, but still don't understand why NiceGUI behaves differently than plain Quasar. @CrystalWindSnake might be right and Vue is recreating the QStep elements. But I don't see why, because NiceGUI is only calling next()
and previous()
, just like in the Quasar snippet I posted above.
I looked into this issue once again, but still don't understand why NiceGUI behaves differently than plain Quasar. @CrystalWindSnake might be right and Vue is recreating the QStep elements. But I don't see why, because NiceGUI is only calling
next()
andprevious()
, just like in the Quasar snippet I posted above.
NiceGUI not only calls next()
, but also calls the update
method of the stepper
itself. This is causing the entire component to refresh, including all its child elements.
In the following example, when you click next
, the backend will print updating stepper
.
from nicegui import ui
class MyStepper(ui.stepper):
def update(self) -> None:
print("updating stepper")
return super().update()
with MyStepper().props("vertical animated") as stepper:
with ui.step("A"):
with ui.stepper_navigation():
ui.button("Next", on_click=stepper.next)
with ui.step("B"):
with ui.stepper_navigation():
ui.button("Next", on_click=stepper.next)
ui.button("Back", on_click=stepper.previous)
with ui.step("C"):
with ui.stepper_navigation():
ui.button("Back", on_click=stepper.previous)
ui.run()
Ah, I wasn't aware of update()
being called. This is because ui.stepper.LOOPBACK = True
which causes update
being called when the value
changes. So I'd expect the problem being fixed by setting ui.stepper.LOOPBACK = False
(the value is updated by setting the "model-value" directly on the client) or ui.stepper.LOOPBACK = None
(the value is updated automatically by the Vue element). But none of them work.
It seems like calling next
is not enough to change the step:
with ui.element('q-stepper').props('model-value=A vertical animated') as stepper:
with ui.element('q-step').props('name=A title=A'):
ui.button('next', on_click=lambda: stepper.run_method('next'))
with ui.element('q-step').props('name=B title=B'):
ui.button('back', on_click=lambda: stepper.run_method('previous'))
Only when subscribing to the update:model-value
event and setting the new model-value
prop, the step changes:
with ui.element('q-stepper').props('model-value=A vertical animated') \
.on('update:model-value', lambda e: stepper.props(f'model-value={e.args}')) as stepper:
with ui.element('q-step').props('name=A title=A'):
ui.button('next', on_click=lambda: stepper.run_method('next'))
with ui.element('q-step').props('name=B title=B'):
ui.button('back', on_click=lambda: stepper.run_method('previous'))
Now the question remains: What does ui.stepper
do differently? Calling .props()
also leads to an update()
, so this can't be the problem. And ui.stepper
overwrites _handle_value_change
, but removing this code doesn't help.
Description
Vertical stepper's animation seems not good, in Quasor's official demo it works smoothly. I use the latest version of Nicegui 2.3.0
https://github.com/user-attachments/assets/7600407d-e6a0-4cd2-bdf6-8218c39a1dc3