nicegui
nicegui copied to clipboard
Re-Opening dialog doesn't update() inputs
Description
I am trying to "hide" some settings in a dialog, but whenever I reopen the dialog, the initial value from page load is reset, seeming ignoring binds.
But the bind actually works correctly, the input is just displaying it wrong. The label changes as expected when I modify the input, but the input default value (either from the element or from the storage) is "restored" every time the dialog opens.
from nicegui import ui, app
@ui.page("/")
def index():
with ui.dialog() as edit_dialog, ui.card():
text_input = ui.input("Text", value="Default").bind_value(app.storage.user, "text")
ui.button("Edit", on_click=edit_dialog.open)
ui.label().bind_text_from(text_input, "value")
ui.run(storage_secret="123")
I can work around it by manually update all contained inputs when opening the dialog (before or after .open() doesn't actually matter), but that hardly seems intentional.
Oh wow, thanks for reporting this issue, @adosikas!
Here is a reproduction without binding and without app storage:
model = {'text': 'test'}
with ui.dialog() as dialog, ui.card():
input_ = ui.input(value=model['text'], on_change=lambda e: model.update(text=e.value))
ui.button('Edit', on_click=dialog.open)
ui.label().bind_text_from(input_, 'value')
It looks like the problem is that the HTML input doesn't exist while the dialog is closed. Vue re-creates it when the dialog is opened, but somehow an outdated value is set. I wonder if we can reproduce the behavior with a plain Vue app...
A plain Vue app works as expected:
<html>
<head>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<button @click="showDialog = true">Edit</button>
<div v-if="showDialog" style="border: 1px solid black; padding: 10px">
<input id="inputText" v-model="text" />
<button @click="showDialog = false">Close</button>
</div>
<p>Entered Text: {{ text }}</p>
</div>
<script>
Vue.createApp({
data() {
return { text: "test", showDialog: false };
},
}).mount("#app");
</script>
</body>
</html>
A Quasar app works as well:
<html>
<head>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/quasar.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="q-app">
<q-btn label="Edit" @click="showDialog = true"></q-btn>
<q-dialog v-model="showDialog">
<q-card>
<q-input v-model="text"></q-input>
<q-btn label="Close" @click="showDialog = false"></q-btn>
</q-card>
</q-dialog>
<div>Entered Text: {{ text }}</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/quasar.umd.js"></script>
<script>
const app = Vue.createApp({
data() {
return { text: "test", showDialog: false };
},
});
app.use(Quasar);
app.mount("#q-app");
</script>
</body>
</html>
The difference might be that NiceGUI doesn't use v-model...
Good to see the problem here, I totally forgot to report this issue.
My use case was also an edit button for some data that opened a dialog: first time user edits an initial value 11 to 22, next time when they click the edit button, they do not see 22 but 11 inside the dialog.
My workaround was to create the dialog each time:
from nicegui import ui
def create_dialog():
with ui.dialog(value=True) as dialog, ui.card():
ui.input(value=model['text'], on_change=lambda e: model.update(text=e.value))
model = {'text': 'test'}
ui.button('Edit', on_click=create_dialog)
ui.label().bind_text_from(model, 'text')
ui.run()
hello! this issue is fixed? i have problem with dialog.update (version 1.4.18) thank you
@rayuel No, this issue is still open and we're looking for ideas and suggestions on how to fix it.
def searchtargetlearn():
serialsend(findtargetresult, sport)
serialsend(distancetotarget, sport)
ans = re.findall(r"[-+]?(?:\d*\.*\d+)", serialsend(measuretarget, sport))
print (ans)
learnHz.set_value(ans[4])
learnV.set_value(ans[5])
learnD.set_value(ans[6])
print (learnHz.value)
learndialog.update()
with ui.dialog() as learndialog, ui.card():
ui.label('LEARN WINDOW')
with ui.row():
learnname = ui.input(label='NAME', placeholder='start typing target name...')
gtargettype = ui.button('IR', on_click=changetargettype)
with ui.column():
learnHz = ui.input(label='Hz', value = '0', placeholder = 'Hz value...')
learnV = ui.input(label='V', value = '0',placeholder = 'V value...')
learnD = ui.input(label='D', value = '0',placeholder = 'D value...')
with ui.column():
searchbutton = ui.button(icon='add_task', on_click=searchtargetlearn).tooltip('Learn new point').classes('bg-green')
savelearnbutton = ui.button(icon='save_as', on_click=savelearn).tooltip('Save result').classes('bg-green')
ui.button('Close', on_click=learndialog.close)
Sometimes dialog button do update sometimes not. all value for dialog i print and check
@rayuel I don't understand how to reproduce that problem, what you expect to happen and what happens instead. Do you think this is the same bug as described in this thread? If not, please open a new Q&A and provide a minimal reproducible example so we can help more efficiently. Thanks!
@falkoschindler Hi,I make a change on this bug.
It seems that ui.input in dialog won't update its value when you input it.
But, when other event triggers bind_value_from of the same ui.input in dialog, its value is updated.
model = {'text': 'test'}
with ui.dialog() as dialog,ui.card():
for i in range(5):
ui.input(label=str(i)).bind_value(model,'text')
ui.button('Edit', on_click=dialog.open)
ui.label().bind_text_from(model,'text')
I'm having the same issue with ui.input binding, but within ui.menu instead of ui.dialog. Every time the menu_item is rendered with the input, the input value resets to its original state, but ONLY IF the value was changed in the input itself directly. If the value was changed as a result of a binding from another component, it stays and renders correctly. I'm using the latest 2.1.0 version of NiceGUI
On #4045 we just discovered a workaround. Setting LOOPBACK = True for input elements on dialogs fixes the wrong model value after re-opening the dialog. But it can re-introduce #287, causing hiccups when typing quickly:
model = {'text': 'test'}
with ui.dialog() as dialog, ui.card():
input_ = ui.input(value=model['text'], on_change=lambda e: model.update(text=e.value))
input_.LOOPBACK = True
ui.button('Edit', on_click=dialog.open)
ui.label().bind_text_from(input_, 'value')
@falkoschindler perfect, this solved it for me within the ui.menu. Here is my full code, i'm creating a custom DateTime input control (I know there is a standard one, but I need some extra features and flexibity
def date_input_control(label, target_object):
with ui.input(label).bind_value(target_object=target_object,target_name='fulltime') as date_input:
date_input.LOOPBACK = True
with ui.menu().props('no-parent-event') as menu:
with ui.row():
ui.date().bind_value(target_object=target_object, target_name='date')
ui.time(mask='HH:mm:ss').bind_value(target_object=target_object, target_name='time')
with ui.row().classes('justify-center'):
ui.button('Close', on_click=menu.close).props('flat')
with date_input.add_slot('append'):
ui.icon('edit_calendar').on('click', menu.toggle).classes('cursor-pointer')
Same problem for ui.input and ui.textarea here, ui.number gets bound value correctly after reopen.
This is my workaround:
- place the dialog creation in a function
- instant new dialog element as needed
- delete the dialog element after it's closed