zauberzeug / nicegui

Create web-based user interfaces with Python. The nice way.
https://nicegui.io
MIT License
10.26k stars 612 forks source link

Re-Opening dialog doesn't update() inputs #2149

Open adosikas opened 11 months ago

adosikas commented 11 months ago

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.

falkoschindler commented 11 months ago

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...

falkoschindler commented 11 months ago

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>
falkoschindler commented 11 months ago

A Quasar app works as well:

<html>
  <head>
    <link href="https://cdn.jsdelivr.net/npm/quasar@2.14.1/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/quasar@2.14.1/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>
falkoschindler commented 11 months ago

The difference might be that NiceGUI doesn't use v-model...

meslahik commented 11 months ago

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()
rayuel commented 8 months ago

hello! this issue is fixed? i have problem with dialog.update (version 1.4.18) thank you

falkoschindler commented 8 months ago

@rayuel No, this issue is still open and we're looking for ideas and suggestions on how to fix it.

rayuel commented 8 months ago
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

falkoschindler commented 8 months ago

@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!

python-and-fiction commented 3 months ago

@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')

动画

aabrodskiy commented 2 months ago

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

falkoschindler commented 4 days ago

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')