]> git.proxmox.com Git - ui/proxmox-yew-widget-toolkit.git/commitdiff
ManagedField: extract validation args
authorDietmar Maurer <dietmar@proxmox.com>
Tue, 10 Oct 2023 14:55:14 +0000 (16:55 +0200)
committerDietmar Maurer <dietmar@proxmox.com>
Tue, 10 Oct 2023 14:55:14 +0000 (16:55 +0200)
to simplify change tracking

src/widget/form/boolean2.rs
src/widget/form/checkbox2.rs
src/widget/form/field2.rs
src/widget/form/managed_field.rs
src/widget/form/number.rs
src/widget/form/selector2.rs
src/widget/form/textarea.rs
src/widget/form/tristate_boolean.rs
src/widget/menu/menu_checkbox2.rs

index bd5a900aa87c6c977900b7dc59b649a55def0167..7b5972778940bc16e34882a1d0706a8c062b5bae 100644 (file)
@@ -63,10 +63,9 @@ pub struct BooleanField {}
 impl ManagedField for BooleanField {
     type Properties = Boolean;
     type Message = Msg;
+    type ValidateClosure = ();
 
-    fn validation_fn_need_update(_props: &Self::Properties, _old_props: &Self::Properties) -> bool {
-        false
-    }
+    fn validation_args(_props: &Self::Properties) -> Self::ValidateClosure { () }
 
     fn setup(props: &Boolean) -> ManagedFieldState {
         let mut value = false;
index c5adabc017be79fbe8244eac566fb23b37a648ee..83050be6077b0f401edfb10370748487239f1280 100644 (file)
@@ -79,10 +79,9 @@ pub struct CheckboxField {}
 impl ManagedField for CheckboxField {
     type Message = Msg;
     type Properties = Checkbox;
+    type ValidateClosure = ();
 
-    fn validation_fn_need_update(_props: &Self::Properties, _old_props: &Self::Properties) -> bool {
-        false
-    }
+    fn validation_args(_props: &Self::Properties) -> Self::ValidateClosure { () }
 
     fn setup(props: &Checkbox) -> ManagedFieldState {
         let on_value = props.value.as_deref().unwrap_or("on").to_string();
index cfdace4b821d7c3648904278bda9e687d878d507..30a6330373b42e7e97ddb08bf5d0873610732052 100644 (file)
@@ -175,20 +175,31 @@ fn value_to_text(value: &Value) -> String {
     }
 }
 
+#[derive(PartialEq)]
+pub struct ValidateClosure {
+    required: bool,
+    input_type: AttrValue,
+    min: Option<f64>,
+    max: Option<f64>,
+    validate: Option<ValidateFn<String>>,
+}
+
 impl ManagedField for StandardField {
     type Properties = Field;
     type Message = Msg;
-
-    fn validation_fn_need_update(props: &Self::Properties, old_props: &Self::Properties) -> bool {
-        props.input_props.required != old_props.input_props.required
-            || props.input_type != old_props.input_type
-            || props.min != old_props.min
-            || props.max != old_props.max
-            || props.validate != old_props.validate
+    type ValidateClosure = ValidateClosure;
+
+    fn validation_args(props: &Self::Properties) -> Self::ValidateClosure {
+        ValidateClosure {
+            required: props.input_props.required,
+            input_type: props.input_type.clone(),
+            min: props.min,
+            max: props.max,
+            validate: props.validate.clone(),
+        }
     }
 
-    fn create_validation_fn(props: &Field) -> ValidateFn<Value> {
-        let props = props.clone();
+    fn create_validation_fn(props: Self::ValidateClosure) -> ValidateFn<Value> {
         ValidateFn::new(move |value: &Value| {
             let value = match value {
                 Value::Null => String::new(),
@@ -202,7 +213,7 @@ impl ManagedField for StandardField {
             };
 
             if value.is_empty() {
-                if props.input_props.required {
+                if props.required {
                     return Err(Error::msg(tr!("Field may not be empty.")));
                 } else {
                     return Ok(());
index de756739950b250b3ed585c8618971b5cef88875..c7b4fef2da056693165415e928ae39e96d67d052 100644 (file)
@@ -155,12 +155,16 @@ impl<MF: ManagedField + Sized> Clone for ManagedFieldLink<MF> {
 pub trait ManagedField: Sized {
     type Properties: Properties + FieldBuilder;
     type Message: 'static;
+    type ValidateClosure: 'static + PartialEq;
 
-    fn validation_fn_need_update(props: &Self::Properties, old_props: &Self::Properties) -> bool {
-        props != old_props
-    }
+    /// Extract arguments passed to the [create_validation_fn](Self::create_validation_fn)
+    ///
+    /// This is called when component properties changes. We rebuild the
+    /// validation function if returend value changes.
+    fn validation_args(props: &Self::Properties) -> Self::ValidateClosure;
 
-    fn create_validation_fn(_props: &Self::Properties) -> ValidateFn<Value> {
+    /// Create the validation function.
+    fn create_validation_fn(_props: Self::ValidateClosure) -> ValidateFn<Value> {
         crate::static_validation_fn!(Value, |_| Ok(()))
     }
 
@@ -291,7 +295,8 @@ impl<MF: ManagedField + 'static> Component for ManagedFieldMaster<MF> {
     fn create(ctx: &Context<Self>) -> Self {
         let props = ctx.props();
 
-        let validate = MF::create_validation_fn(props);
+        let validation_args = MF::validation_args(props);
+        let validate = MF::create_validation_fn(validation_args);
 
         let mut comp_state = MF::setup(props);
         comp_state.valid = validate
@@ -462,9 +467,12 @@ impl<MF: ManagedField + 'static> Component for ManagedFieldMaster<MF> {
                 refresh1 = true;
             }
 
-            if MF::validation_fn_need_update(props, old_props) {
+            let old_validation_args = MF::validation_args(old_props);
+            let validation_args = MF::validation_args(props);
+
+            if validation_args != old_validation_args {
                 log::info!("UPDATE VF {:?}", input_props.name);
-                let validate = MF::create_validation_fn(props);
+                let validate = MF::create_validation_fn(validation_args);
                 if validate != self.validate {
                     refresh1 = true;
                     field_handle.update_validate(Some(validate));
index 1bead2c6595e373658c17fbbe77d308f8338a86a..53b0cfa395996af746f4fdc7ed2ee5a596d031d4 100644 (file)
@@ -300,19 +300,29 @@ fn value_to_number(value: Value) -> Value {
     }
 }
 
+#[derive(PartialEq)]
+pub struct ValidateClosure<T> {
+    required: bool,
+    min: Option<T>,
+    max: Option<T>,
+    validate: Option<ValidateFn<T>>,
+}
+
 impl<T: NumberTypeInfo> ManagedField for NumberField<T> {
     type Properties = Number<T>;
     type Message = Msg;
+    type ValidateClosure = ValidateClosure<T>;
+
+    fn validation_args(props: &Self::Properties) -> Self::ValidateClosure {
+        ValidateClosure {
+            required: props.input_props.required,
+            min: props.min,
+            max: props.max,
+            validate: props.validate.clone(),
+        }
+     }
 
-    fn validation_fn_need_update(props: &Self::Properties, old_props: &Self::Properties) -> bool {
-        props.input_props.required != old_props.input_props.required
-            || props.min != old_props.min
-            || props.max != old_props.max
-            || props.validate != old_props.validate
-    }
-
-    fn create_validation_fn(props: &Number<T>) -> ValidateFn<Value> {
-        let props = props.clone();
+    fn create_validation_fn(props: Self::ValidateClosure) -> ValidateFn<Value> {
         ValidateFn::new(move |value: &Value| {
             let is_empty = match value {
                 Value::Null => true,
@@ -322,7 +332,7 @@ impl<T: NumberTypeInfo> ManagedField for NumberField<T> {
             };
 
             if is_empty {
-                if props.input_props.required {
+                if props.required {
                     return Err(Error::msg(tr!("Field may not be empty.")));
                 } else {
                     return Ok(());
index 97a3f65f3a1c17aa79602850fa90f41a331be80e..077d1728ea99f515f2d05b16470bfa29624834d4 100644 (file)
@@ -174,20 +174,28 @@ impl<S: DataStore + 'static> SelectorField<S> {
     }
 }
 
+#[derive(PartialEq)]
+pub struct ValidateClosure<S: DataStore> {
+    required: bool,
+    store: S,
+    validate: Option<ValidateFn<(String, S)>>,
+}
+
 impl<S: DataStore + 'static> ManagedField for SelectorField<S> {
     type Message = Msg<S>;
     type Properties = Selector<S>;
+    type ValidateClosure = ValidateClosure<S>;
 
-    fn validation_fn_need_update(props: &Self::Properties, old_props: &Self::Properties) -> bool {
-        props.input_props.required != old_props.input_props.required
-            || props.store != old_props.store
-            || props.validate != old_props.validate
+    fn validation_args(props: &Self::Properties) -> Self::ValidateClosure {
+        ValidateClosure {
+            required: props.input_props.required,
+            store: props.store.clone(),
+            validate: props.validate.clone(),
+        }
     }
 
-    fn create_validation_fn(props: &Selector<S>) -> ValidateFn<Value> {
-        let store = props.store.clone();
-        let required = props.input_props.required;
-        let validate = props.validate.clone();
+    fn create_validation_fn(props: Self::ValidateClosure) -> ValidateFn<Value> {
+
         ValidateFn::new(move |value: &Value| {
             let value = match value {
                 Value::Null => String::new(),
@@ -200,16 +208,16 @@ impl<S: DataStore + 'static> ManagedField for SelectorField<S> {
             };
 
             if value.is_empty() {
-                if required {
+                if props.required {
                     bail!("Field may not be empty.");
                 } else {
                     return Ok(());
                 }
             }
 
-            if !store.is_empty() {
-                match &validate {
-                    Some(cb) => cb.validate(&(value.into(), store.clone())),
+            if !props.store.is_empty() {
+                match &props.validate {
+                    Some(cb) => cb.validate(&(value.into(), props.store.clone())),
                     None => Ok(()),
                 }
             } else {
index 4b550056682a94a1948c49b8ea056195ca4d80d8..b0a819d37fe2cdfadd91161afc1cf6764fdd4392 100644 (file)
@@ -102,13 +102,25 @@ fn value_to_text(value: &Value) -> String {
     }
 }
 
+#[derive(PartialEq)]
+pub struct ValidateClosure {
+    required: bool,
+    validate: Option<ValidateFn<String>>,
+}
+
 impl ManagedField for TextAreaField {
     type Properties = TextArea;
     type Message = Msg;
+    type ValidateClosure = ValidateClosure;
+
+    fn validation_args(props: &Self::Properties) -> Self::ValidateClosure {
+        ValidateClosure {
+            required: props.input_props.required,
+            validate: props.validate.clone(),
+        }
+    }
 
-    fn create_validation_fn(props: &TextArea) -> ValidateFn<Value> {
-        let input_props = props.input_props.clone();
-        let validate = props.validate.clone();
+    fn create_validation_fn(props: ValidateClosure) -> ValidateFn<Value> {
         ValidateFn::new(move |value: &Value| {
             let value = match value {
                 Value::Null => String::new(),
@@ -121,14 +133,14 @@ impl ManagedField for TextAreaField {
             };
 
             if value.is_empty() {
-                if input_props.required {
+                if props.required {
                     return Err(Error::msg(tr!("Field may not be empty.")));
                 } else {
                     return Ok(());
                 }
             }
 
-            match &validate {
+            match &props.validate {
                 Some(cb) => cb.validate(&value),
                 None => Ok(()),
             }
index f670da8deac35d87b62542b42d967be523f63e65..5eee98ebe620bdea403886f750b9179c1d162106 100644 (file)
@@ -101,12 +101,11 @@ fn value_to_tristate(value: &Value) -> Option<Tristate> {
 impl ManagedField for PwtTristateBoolean {
     type Message = Msg;
     type Properties = TristateBoolean;
+    type ValidateClosure = ();
 
-    fn validation_fn_need_update(_props: &Self::Properties, _old_props: &Self::Properties) -> bool {
-        false
-    }
+    fn validation_args(_props: &Self::Properties) -> Self::ValidateClosure { () }
 
-    fn create_validation_fn(_props: &Self::Properties) -> ValidateFn<Value> {
+    fn create_validation_fn(_props: Self::ValidateClosure) -> ValidateFn<Value> {
         ValidateFn::new(move |value: &Value| match value {
             Value::Null | Value::Bool(_) => Ok(()),
             _ => Err(Error::msg(tr!("Got wrong data type!"))),
index f6cc4fb10f76556fe5816bac09881f70b58c4da7..edae56a173b21b1aaa16b60b3e32694efd2f18b1 100644 (file)
@@ -109,6 +109,9 @@ pub struct MenuCheckboxField {}
 impl ManagedField for MenuCheckboxField {
     type Message = Msg;
     type Properties = MenuCheckbox;
+    type ValidateClosure = ();
+
+    fn validation_args(_props: &Self::Properties) -> Self::ValidateClosure { () }
 
     fn setup(props: &MenuCheckbox) -> ManagedFieldState {
         let on_value = props.value.as_deref().unwrap_or("on").to_string();