zauberzeug / nicegui

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

`ui.codemirror` not updating when the bound dictionary changes #3337

Open zkx06111 opened 1 month ago

zkx06111 commented 1 month ago

Description

Using nicegui==1.4.28

from nicegui import ui

shared_memory = {
    'code': '// Write your code here',
    'design': '',
    'test': '',
}

@ui.page('/')
def main():
    editor = ui.codemirror(language='python').bind_value(shared_memory, 'code')
    ui.markdown(shared_memory["code"]).bind_content(shared_memory, 'code')
    ui.button('clear code').on('click', lambda: shared_memory.update(code=''))

ui.run()

When the button is clicked the first time, it works as expected. Both the markdown text and the codemirror editor are cleared.

However, when it's clicked the second time, the markdown gets cleared while the codemirror remains the same.

When I try to edit what's in codemirror, I get the following error.

Traceback (most recent call last):
  File "/Users/kexunz/miniconda3/envs/ad/lib/python3.11/site-packages/nicegui/events.py", line 413, in handle_event
    result = handler(arguments) if expects_arguments else handler()
             ^^^^^^^^^^^^^^^^^^
  File "/Users/kexunz/miniconda3/envs/ad/lib/python3.11/site-packages/nicegui/elements/mixins/value_element.py", line 40, in handle_change
    self.set_value(self._event_args_to_value(e))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/kexunz/miniconda3/envs/ad/lib/python3.11/site-packages/nicegui/elements/codemirror.py", line 294, in _event_args_to_value
    new_value = changeset.apply(self.value)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/kexunz/miniconda3/envs/ad/lib/python3.11/site-packages/nicegui/elements/codemirror.py", line 322, in apply
    raise ValueError('Cannot apply change set to a document with the wrong length')
ValueError: Cannot apply change set to a document with the wrong length
python-and-fiction commented 1 month ago

It is a little similar to #3217 . I change LOOPBACK = None in .venv\Lib\site-packages\nicegui\elements\codemirror.py to LOOPBACK = True. It works as expected.

Following code shows that _props['value'] has changed, but the value displayed in ui.codemirror is the same as the value before.

from nicegui import ui

shared_memory = {
    'code': '',
    'design': '',
    'test': '',
}

@ui.page('/')
def main():
    editor = ui.codemirror(language='python').bind_value(shared_memory, 'code')
    ui.markdown(shared_memory["code"]).bind_content(shared_memory, 'code')
    ui.button('clear code').on('click', lambda: shared_memory.update(code=''))
    ui.button('print',on_click=lambda :ui.notify(editor._props['value']))

ui.run()
falkoschindler commented 1 month ago

Thanks for reporting this bug, @zkx06111! It is indeed very similar to #3217. Therefore I'll close this as duplicate and add a ui.codemirror example over there.

falkoschindler commented 1 month ago

It turned out that fixing ui.editor doesn't fix ui.codemirror, even though both problems are very similar. Therefore I'll re-open this issue.

Minimum reproduction:

code = ui.codemirror(value='foo')
ui.label().bind_text_from(code, 'value')
ui.button('Set X', on_click=lambda: code.set_value('X'))

The button only works once.

Somehow we need a similar solution like PR #3346, but combined with the already existing Vue component in codemirror.js.