Skip to content

Commit 1536d95

Browse files
Introduce ValueChangeEventArguments.previous_value (#4456)
Inspired by discussion #4410, this PR introduces a `previous_value` field for `ValueChangeEventArguments`. This change is pretty straightforward and only requires adjustments for two mixins and two elements. Right now it is lacking backward-compatibility though. If user code derives custom `ValueChangeEventArguments`, they are now missing the `previous_value` field. Should we add a default value? But it should be clear that this default is _not_ the previous value, but "not set". We might need a sentinel... And in 3.0 I'd like to enforce the `previous_value` field. A deprecation warning for the default value would be great, but probably hard to achieve.
1 parent 9630826 commit 1536d95

File tree

6 files changed

+40
-6
lines changed

6 files changed

+40
-6
lines changed

nicegui/elements/mixins/selectable_element.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,9 @@ def _handle_selection_change(self, selected: bool) -> None:
102102
103103
:param selected: The new selection state.
104104
"""
105+
previous_value = self._props.get('selected')
105106
self._props['selected'] = selected
106107
self.update()
107-
args = ValueChangeEventArguments(sender=self, client=self.client, value=self._props['selected'])
108+
args = ValueChangeEventArguments(sender=self, client=self.client, value=selected, previous_value=previous_value)
108109
for handler in self._selection_change_handlers:
109110
handle_event(handler, args)

nicegui/elements/mixins/value_element.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,13 @@ def set_value(self, value: Any) -> None:
108108
self.value = value
109109

110110
def _handle_value_change(self, value: Any) -> None:
111+
previous_value = self._props.get(self.VALUE_PROP)
111112
self._props[self.VALUE_PROP] = self._value_to_model_value(value)
112113
if self._send_update_on_value_change:
113114
self.update()
114-
args = ValueChangeEventArguments(sender=self, client=self.client, value=self._value_to_event_value(value))
115+
args = ValueChangeEventArguments(sender=self, client=self.client,
116+
value=self._value_to_event_value(value),
117+
previous_value=self._value_to_event_value(previous_value))
115118
for handler in self._change_handlers:
116119
handle_event(handler, args)
117120

nicegui/elements/table.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,11 @@ def handle_selection(e: GenericEventArguments) -> None:
9090
self.on('selection', handle_selection, ['added', 'rows', 'keys'])
9191

9292
def handle_pagination_change(e: GenericEventArguments) -> None:
93+
previous_value = self.pagination
9394
self.pagination = e.args
9495
self.update()
95-
arguments = ValueChangeEventArguments(sender=self, client=self.client, value=self.pagination)
96+
arguments = ValueChangeEventArguments(sender=self, client=self.client,
97+
value=self.pagination, previous_value=previous_value)
9698
for handler in self._pagination_change_handlers:
9799
handle_event(handler, arguments)
98100
self.on('update:pagination', handle_pagination_change)

nicegui/elements/tree.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,21 +63,30 @@ def update_prop(name: str, value: Any) -> None:
6363
self.update()
6464

6565
def handle_selected(e: GenericEventArguments) -> None:
66+
previous_value = self._props.get('selected')
6667
update_prop('selected', e.args)
68+
args = ValueChangeEventArguments(sender=self, client=self.client,
69+
value=e.args, previous_value=previous_value)
6770
for handler in self._select_handlers:
68-
handle_event(handler, ValueChangeEventArguments(sender=self, client=self.client, value=e.args))
71+
handle_event(handler, args)
6972
self.on('update:selected', handle_selected)
7073

7174
def handle_expanded(e: GenericEventArguments) -> None:
75+
previous_value = self._props.get('expanded')
7276
update_prop('expanded', e.args)
77+
args = ValueChangeEventArguments(sender=self, client=self.client,
78+
value=e.args, previous_value=previous_value)
7379
for handler in self._expand_handlers:
74-
handle_event(handler, ValueChangeEventArguments(sender=self, client=self.client, value=e.args))
80+
handle_event(handler, args)
7581
self.on('update:expanded', handle_expanded)
7682

7783
def handle_ticked(e: GenericEventArguments) -> None:
84+
previous_value = self._props.get('ticked')
7885
update_prop('ticked', e.args)
86+
args = ValueChangeEventArguments(sender=self, client=self.client,
87+
value=e.args, previous_value=previous_value)
7988
for handler in self._tick_handlers:
80-
handle_event(handler, ValueChangeEventArguments(sender=self, client=self.client, value=e.args))
89+
handle_event(handler, args)
8190
self.on('update:ticked', handle_ticked)
8291

8392
def on_select(self, callback: Handler[ValueChangeEventArguments]) -> Self:

nicegui/events.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,13 @@ class MultiUploadEventArguments(UiEventArguments):
134134
@dataclass(**KWONLY_SLOTS)
135135
class ValueChangeEventArguments(UiEventArguments):
136136
value: Any
137+
previous_value: Any = ...
138+
139+
def __post_init__(self):
140+
# DEPRECATED: previous_value will be required in NiceGUI 4.0
141+
if self.previous_value is ...:
142+
helpers.warn_once('The new event argument `ValueChangeEventArguments.previous_value` is not set. '
143+
'In NiceGUI 4.0 this will raise an error.')
137144

138145

139146
@dataclass(**KWONLY_SLOTS)

tests/test_events.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,15 @@ def test_delegated_event_with_argument_filtering(screen: Screen) -> None:
206206
screen.click('Item B')
207207
screen.click('Item C')
208208
assert ids == ['A', 'B', 'C']
209+
210+
211+
def test_value_change_event_arguments(screen: Screen):
212+
events = []
213+
ui.checkbox('Checkbox', on_change=lambda e: events.append((e.value, e.previous_value)))
214+
215+
screen.open('/')
216+
screen.click('Checkbox')
217+
assert events == [(True, False)]
218+
219+
screen.click('Checkbox')
220+
assert events == [(True, False), (False, True)]

0 commit comments

Comments
 (0)