Repository: zauberzeug/nicegui
Pull request #4379 (2025-02-22 11:47:15)

improve update of notifications
As noticed in #4373, notifications received an update when another notification was created. This update called `this.notification()`, causing progress bars to reset. This PR solves this problem by explicitly calling an `update_notification()` method on UI updates, but preventing the accidental updates caused by other notifications.

Can be tested with a simple `ui.notification`:
```py
ui.button('Notify', on_click=lambda: ui.notification('Hi!'))
```
Clicking the button again and again will prevent any notification from disappearing after the default timeout of 5 seconds.

(By the way: I removed an obsolete "data-id" attribute. This hasn't been in use since #2983.)

rodja: > Clicking the button again and again will prevent any notification from disappearing after the default timeout of 5 seconds.

Strange. I can not reproduce the described issue on the main branch:

![Feb-23-2025 14-49-46](https://github.com/user-attachments/assets/4429df6b-015b-402d-8f5c-1adc27be16f4)
rodja: Ah, seems that the issue does not happen on auto-index pages. I could reproduce it with a page builder. Strangely the test still fails on main -- even if it does not use a page builder.
falkoschindler: > Ah, seems that the issue does not happen on auto-index pages.

Yes it does. Another example:

```py
ui.timer(1, lambda: ui.notification('Hi!', timeout=3))
```

There shouldn't be more than 3 or 4 notifications on the screen at the same time. Yet they don't disappear.

Patch:
diff --git a/nicegui/elements/notification.js b/nicegui/elements/notification.js
index 73c62030..e5acf945 100644
--- a/nicegui/elements/notification.js
+++ b/nicegui/elements/notification.js
@@ -4,10 +4,10 @@ export default {
   mounted() {
     this.notification = Quasar.Notify.create(this.convertedOptions);
   },
-  updated() {
-    this.notification(this.convertedOptions);
-  },
   methods: {
+    update_notification() {
+      this.notification(this.convertedOptions);
+    },
     dismiss() {
       this.notification();
     },
diff --git a/nicegui/elements/notification.py b/nicegui/elements/notification.py
index 61203890..aa1e0eb4 100644
--- a/nicegui/elements/notification.py
+++ b/nicegui/elements/notification.py
@@ -77,7 +77,6 @@ class Notification(Element, component='notification.js'):
                 'closeBtn': close_button,
                 'timeout': (timeout or 0) * 1000,
                 'group': False,
-                'attrs': {'data-id': f'nicegui-dialog-{self.id}'},
             }
             if type is not None:
                 self._props['options']['type'] = type
@@ -199,3 +198,7 @@ class Notification(Element, component='notification.js'):
 
     def set_visibility(self, visible: bool) -> None:
         raise NotImplementedError('Use `dismiss()` to remove the notification. See #3670 for more information.')
+
+    def update(self) -> None:
+        super().update()
+        self.run_method('update_notification')


Test patch:
diff --git a/tests/test_notification.py b/tests/test_notification.py
index eb62c8e1..5aceba35 100644
--- a/tests/test_notification.py
+++ b/tests/test_notification.py
@@ -37,3 +37,22 @@ def test_dismiss(screen: Screen):
     screen.wait(1.5)
     screen.should_not_contain('Hi!')
     assert len(b.client.layout.default_slot.children) == 1
+
+
+def test_no_reset_by_other_notifications(screen: Screen):
+    # see #4373
+    ui.button('Button A', on_click=lambda: ui.notification('Notification A', timeout=1.0))
+    ui.button('Button B', on_click=lambda: ui.notification('Notification B', timeout=1.0))
+    ui.button('Button C', on_click=lambda: ui.notification('Notification C', timeout=1.0))
+    ui.button('Button D', on_click=lambda: ui.notification('Notification D', timeout=1.0))
+
+    screen.open('/')
+    screen.click('Button A')
+    screen.wait(1)
+    screen.click('Button B')
+    screen.wait(1)
+    screen.click('Button C')
+    screen.wait(1)
+    screen.click('Button D')
+    screen.should_contain('Notification D')
+    screen.should_not_contain('Notification A')