浏览代码

Merge pull request #2133 from steweg/feature/enhanced_validations

Adjusting validation element for better err_msg
Falko Schindler 1 年之前
父节点
当前提交
303184817a

+ 25 - 7
nicegui/elements/mixins/validation_element.py

@@ -1,11 +1,11 @@
-from typing import Any, Callable, Dict, Optional
+from typing import Any, Callable, Dict, Optional, Union
 
 from .value_element import ValueElement
 
 
 class ValidationElement(ValueElement):
 
-    def __init__(self, validation: Optional[Dict[str, Callable[..., bool]]], **kwargs: Any) -> None:
+    def __init__(self, validation: Optional[Union[Callable[..., Optional[str]], Dict[str, Callable[..., bool]]]], **kwargs: Any) -> None:
         super().__init__(**kwargs)
         self.validation = validation if validation is not None else {}
         self._error: Optional[str] = None
@@ -15,16 +15,34 @@ class ValidationElement(ValueElement):
         """The latest error message from the validation functions."""
         return self._error
 
+    @error.setter
+    def error(self, error: Optional[str]) -> None:
+        """Sets the error message.
+
+        :param error: The optional error message
+        """
+        self._error = error
+        if self._error is None:
+            self.props(remove='error')
+        else:
+            self._props['error-message'] = self._error
+            self.props('error')
+
     def validate(self) -> bool:
-        """Validate the current value and set the error message if necessary."""
+        """Validate the current value and set the error message if necessary.
+
+        :return: True if the value is valid, False otherwise
+        """
+        if callable(self.validation):
+            self.error = self.validation(self.value)
+            return self.error is None
+
         for message, check in self.validation.items():
             if not check(self.value):
-                self._error = message
-                self.props(f'error error-message="{message}"')
+                self.error = message
                 return False
 
-        self._error = None
-        self.props(remove='error')
+        self.error = None
         return True
 
     def _handle_value_change(self, value: Any) -> None:

+ 7 - 2
tests/test_input.py

@@ -1,3 +1,4 @@
+import pytest
 from selenium.webdriver.common.by import By
 from selenium.webdriver.common.keys import Keys
 
@@ -53,8 +54,12 @@ def test_toggle_button(screen: Screen):
     assert element.get_attribute('type') == 'password'
 
 
-def test_input_validation(screen: Screen):
-    input_ = ui.input('Name', validation={'Too short': lambda value: len(value) >= 5})
+@pytest.mark.parametrize('use_callable', [False, True])
+def test_input_validation(use_callable: bool, screen: Screen):
+    if use_callable:
+        input_ = ui.input('Name', validation=lambda value: 'Too short' if len(value) < 5 else None)
+    else:
+        input_ = ui.input('Name', validation={'Too short': lambda value: len(value) >= 5})
 
     screen.open('/')
     screen.should_contain('Name')

+ 11 - 0
website/documentation/content/input_documentation.py

@@ -42,4 +42,15 @@ def styling():
             .props('flat dense').bind_visibility_from(i, 'value')
 
 
+@doc.demo('Input validation', '''
+    You can validate the input in two ways:
+
+    - by passing a callable that returns an error message or `None`, or
+    - by passing a dictionary that maps error messages to callables that return `True` (error) or `False` (no error).
+''')
+def validation():
+    ui.input('Name', validation=lambda value: 'Too short' if len(value) < 5 else None)
+    ui.input('Name', validation={'Too short': lambda value: len(value) >= 5})
+
+
 doc.reference(ui.input)