nicegui icon indicating copy to clipboard operation
nicegui copied to clipboard

Re-Opening dialog doesn't update() inputs

Open adosikas opened this issue 1 year ago • 11 comments

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.

adosikas avatar Dec 10 '23 12:12 adosikas

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 avatar Dec 12 '23 06:12 falkoschindler

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 avatar Dec 12 '23 06:12 falkoschindler

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>

falkoschindler avatar Dec 12 '23 06:12 falkoschindler

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

falkoschindler avatar Dec 12 '23 08:12 falkoschindler

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

meslahik avatar Dec 12 '23 08:12 meslahik

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

rayuel avatar Mar 11 '24 10:03 rayuel

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

falkoschindler avatar Mar 11 '24 10:03 falkoschindler

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 avatar Mar 11 '24 10:03 rayuel

@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 avatar Mar 11 '24 11:03 falkoschindler

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

动画

python-and-novella avatar Aug 22 '24 08:08 python-and-novella

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

aabrodskiy avatar Sep 18 '24 18:09 aabrodskiy

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 avatar Nov 29 '24 07:11 falkoschindler

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

aabrodskiy avatar Dec 09 '24 12:12 aabrodskiy

Same problem for ui.input and ui.textarea here, ui.number gets bound value correctly after reopen.

This is my workaround:

  1. place the dialog creation in a function
  2. instant new dialog element as needed
  3. delete the dialog element after it's closed

liunux4odoo avatar Dec 10 '24 03:12 liunux4odoo