]> git.proxmox.com Git - ui/proxmox-yew-widget-toolkit.git/commitdiff
FormContext: replace submit_converter with SubmitValidateFn
authorDietmar Maurer <dietmar@proxmox.com>
Sun, 22 Oct 2023 13:51:02 +0000 (15:51 +0200)
committerDietmar Maurer <dietmar@proxmox.com>
Sun, 22 Oct 2023 13:51:02 +0000 (15:51 +0200)
Simply let the validation function return the value to be submitted.

13 files changed:
src/widget/form/boolean.rs
src/widget/form/checkbox.rs
src/widget/form/context.rs
src/widget/form/field.rs
src/widget/form/hidden.rs
src/widget/form/managed_field.rs
src/widget/form/mod.rs
src/widget/form/number.rs
src/widget/form/selector.rs
src/widget/form/submit_validate.rs [new file with mode: 0644]
src/widget/form/textarea.rs
src/widget/form/tristate_boolean.rs
src/widget/menu/menu_checkbox.rs

index 239a0ff0bc3315a5d3fbf86b66a50f56f6e668e4..a8cbd1a66543cbb46736feceece71a8c1d7185df 100644 (file)
@@ -84,7 +84,6 @@ impl ManagedField for BooleanField {
             value: value.into(), valid, default,
             radio_group: false,
             unique: false,
-            submit_converter: None,
         }
     }
 
index 87b1f5171e3cc1433114ee289d4c2bd9ad133f93..a0aa954b60d4ce94b323f7e7f3003466c390b616 100644 (file)
@@ -103,7 +103,6 @@ impl ManagedField for CheckboxField {
             default: default.into(),
             radio_group: props.radio_group,
             unique: false,
-            submit_converter: None,
         }
     }
 
index 89de3e3bcd20df403c8050f88e7bcf2e96bc5a36..e953cc63b01e593e6f20ccc19cbfafd7e27fc055 100644 (file)
@@ -15,7 +15,7 @@ use yew::prelude::*;
 
 use crate::state::optional_rc_ptr_eq;
 
-use super::ValidateFn;
+use super::SubmitValidateFn;
 
 /// Basic field options used inside [FormContext].
 ///
@@ -39,7 +39,7 @@ struct FieldRegistration {
     // Field name.
     pub name: AttrValue,
     /// The validation function
-    pub validate: Option<ValidateFn<Value>>,
+    pub validate: Option<SubmitValidateFn<Value>>,
     /// Radio group flag. Set this when the field is part of a radio group.
     pub radio_group: bool,
     /// Do not allow multiple fields with the same name.
@@ -50,29 +50,43 @@ struct FieldRegistration {
     pub options: FieldOptions,
     /// Field value
     pub value: Value,
+    // Submit value (value returned by validate)
+    submit_value: Option<Value>,
     /// Field default value.
     pub default: Value,
     /// Validation result.
     pub valid: Result<(), String>,
-    /// Optional conversion hook called by [FormContext::get_submit_data]
-    ///
-    /// Should return `None` if value is invalid.
-    pub submit_converter: Option<Callback<Value, Option<Value>>>,
 }
 
 impl FieldRegistration {
     fn is_dirty(&self) -> bool {
         // we need to compare the value that will be submitted
-        let submit_value = match &self.submit_converter  {
-            Some(convert) => {
-                match convert.emit(self.value.clone()) {
-                    Some(v) => v,
-                    None => return true,
+        match &self.submit_value {
+            Some(submit_value) => &self.default != submit_value,
+            None => true,
+        }
+    }
+
+    fn apply_value(&mut self, value: Value) {
+        let (valid, submit_value);
+        if let Some(validate) = &self.validate {
+            match validate.apply(&value).map_err(|e| e.to_string()) {
+                Ok(value) => {
+                    submit_value = Some(value);
+                    valid = Ok(());
+                }
+                Err(e) => {
+                    submit_value = None;
+                    valid = Err(e.to_string());
                 }
             }
-            None => self.value.clone(),
-        };
-        self.default != submit_value
+        } else {
+            submit_value = Some(value.clone());
+            valid = Ok(());
+        }
+        self.value = value;
+        self.valid = valid;
+        self.submit_value = submit_value;
     }
 }
 /// Shared form data ([Rc]<[RefCell]<[FormContextState]>>)
@@ -182,7 +196,7 @@ impl FieldHandle {
         self.write().validate_field_by_slab_key(key);
     }
     /// Update validation function and trigger re-validation
-    pub fn update_validate(&mut self, validate: Option<ValidateFn<Value>>) {
+    pub fn update_validate(&mut self, validate: Option<SubmitValidateFn<Value>>) {
         let key = self.key;
         self.write()
             .update_field_validate_by_slab_key(key, validate);
@@ -274,10 +288,9 @@ impl FormContext {
         value: Value,
         default: Value,
         radio_group: bool,
-        validate: Option<ValidateFn<Value>>,
+        validate: Option<SubmitValidateFn<Value>>,
         options: FieldOptions,
         unique: bool,
-        submit_converter: Option<Callback<Value, Option<Value>>>,
     ) -> FieldHandle {
         let key = self.inner.borrow_mut().register_field(
             name,
@@ -287,7 +300,6 @@ impl FormContext {
             validate,
             options,
             unique,
-            submit_converter,
         );
 
         FieldHandle {
@@ -419,32 +431,28 @@ impl FormContextState {
         value: Value,
         default: Value,
         radio_group: bool,
-        validate: Option<ValidateFn<Value>>,
+        validate: Option<SubmitValidateFn<Value>>,
         options: FieldOptions,
         unique: bool,
-        submit_converter: Option<Callback<Value, Option<Value>>>,
     ) -> usize {
         let name = name.into_prop_value();
 
         let unique = if radio_group { false } else { unique };
 
-        let mut valid = Ok(());
-        if let Some(validate) = &validate {
-            valid = validate.apply(&value).map_err(|e| e.to_string());
-        }
-
-        let field = FieldRegistration {
+        let mut field = FieldRegistration {
             name: name.clone(),
             validate,
             radio_group,
             unique,
             options,
-            value,
+            value: Value::Null, // set by apply_value below
+            submit_value: None, // set by apply_value below
             default: default.clone(),
-            valid,
-            submit_converter,
+            valid: Ok(()), // set by apply_value below
         };
 
+        field.apply_value(value);
+
         let slab_key;
 
         if unique {
@@ -610,14 +618,7 @@ impl FormContextState {
                 self.version += 1;
             }
             if value != field.value {
-                let mut valid = Ok(());
-                if let Some(validate) = &field.validate {
-                    valid = validate.apply(&value).map_err(|e| e.to_string());
-                }
-
-                field.value = value;
-                field.valid = valid;
-
+                field.apply_value(value);
                 self.version += 1;
             }
         }
@@ -641,12 +642,7 @@ impl FormContextState {
         let field = &mut self.fields[slab_key];
         if field.value != field.default {
             self.version += 1;
-            field.value = field.default.clone();
-            if let Some(validate) = &field.validate {
-                field.valid = validate.apply(&field.value).map_err(|e| e.to_string());
-            } else {
-                field.valid = Ok(());
-            }
+            field.apply_value(field.default.clone());
         }
     }
 
@@ -680,12 +676,7 @@ impl FormContextState {
         for (_key, field) in self.fields.iter_mut() {
             if field.value != field.default {
                 changes = true;
-                field.value = field.default.clone();
-                let mut valid = Ok(());
-                if let Some(validate) = &field.validate {
-                    valid = validate.apply(&field.value).map_err(|e| e.to_string());
-                }
-                field.valid = valid;
+                field.apply_value(field.default.clone());
             }
         }
         if changes {
@@ -713,21 +704,37 @@ impl FormContextState {
         if field.radio_group {
             // fixme: do something ?
         } else {
-            let mut valid = Ok(());
+            let (valid, submit_value);
             if let Some(validate) = &field.validate {
-                valid = validate.apply(&field.value).map_err(|e| e.to_string());
+                match validate.apply(&field.value) {
+                    Ok(value) => {
+                        submit_value = Some(value);
+                        valid = Ok(());
+                    }
+                    Err(e) => {
+                        submit_value = None;
+                        valid = Err(e.to_string());
+                    }
+                }
+            } else {
+                submit_value = Some(field.value.clone());
+                valid = Ok(());
             }
             if valid != field.valid {
                 self.version += 1;
                 field.valid = valid;
             }
-        }
+            if submit_value != field.submit_value {
+                self.version += 1;
+                field.submit_value = submit_value;
+            }
+       }
     }
 
     fn update_field_validate_by_slab_key(
         &mut self,
         slab_key: usize,
-        validate: Option<ValidateFn<Value>>,
+        validate: Option<SubmitValidateFn<Value>>,
     ) {
         let field = &mut self.fields[slab_key];
         field.validate = validate;
@@ -863,22 +870,15 @@ impl FormContextState {
                 let field = &self.fields[key];
                 let submit_empty = field.options.submit_empty;
                 if field.valid.is_ok() && field.options.submit {
-                    let mut value = field.value.clone();
-                    if !submit_empty && value_is_empty(&value) {
-                        continue;
-                    }
-                    if let Some(submit_converter) = &field.submit_converter {
-                        value = match submit_converter.emit(value) {
-                            Some(value) => {
-                                if !submit_empty & value_is_empty(&value) {
-                                    continue;
-                                }
-                                value
+                     match &field.submit_value {
+                        None => continue,
+                        Some(value) => {
+                            if !submit_empty & value_is_empty(&value) {
+                                continue;
                             }
-                            None => continue, // should not happen
-                        };
+                            data[name.deref()] = value.clone();
+                        }
                     }
-                    data[name.deref()] = value;
                 }
                 continue;
             }
@@ -890,22 +890,15 @@ impl FormContextState {
                     let field = &self.fields[key];
                     let submit_empty = field.options.submit_empty;
                     if field.valid.is_ok() && field.options.submit {
-                        let mut value = field.value.clone();
-                        if !submit_empty && value_is_empty(&value) {
-                            continue;
-                        }
-                        if let Some(submit_converter) = &field.submit_converter {
-                            value = match submit_converter.emit(value) {
-                                Some(value) => {
-                                    if !submit_empty && value_is_empty(&value) {
-                                        continue;
-                                    }
-                                    value
+                        match &field.submit_value {
+                            None => continue,
+                            Some(value) => {
+                                if !submit_empty & value_is_empty(&value) {
+                                    continue;
                                 }
-                                None => continue, // should not happen
-                            };
+                                list.push(value.clone());
+                            }
                         }
-                        list.push(value);
                     }
                 }
                 if !list.is_empty() {
index 5014c8133a37925311098a2a0e55f687195c38a4..d8199b0cbab6278116197d7b601e352c81f3fb09 100644 (file)
@@ -301,23 +301,19 @@ impl ManagedField for StandardField {
         }
     }
 
-    fn validator(props: &Self::ValidateClosure, value: &Value) -> Result<(), Error> {
+    fn validator(props: &Self::ValidateClosure, value: &Value) -> Result<Value, Error> {
         let value = match value {
             Value::Null => String::new(),
             Value::Number(n) => n.to_string(),
             Value::String(v) => v.clone(),
-            _ => {
-                // should not happen
-                log::error!("PwtField: got wrong data type in validate!");
-                String::new()
-            }
+            _ => return Err(Error::msg(tr!("got wrong data type."))),
         };
 
         if value.is_empty() {
             if props.required {
                 return Err(Error::msg(tr!("Field may not be empty.")));
             } else {
-                return Ok(());
+                return Ok(Value::String(String::new()));
             }
         }
 
@@ -344,10 +340,11 @@ impl ManagedField for StandardField {
             }
         }
 
-        match &props.validate {
-            Some(validate) => validate.apply(&value),
-            None => Ok(()),
+        if let Some(validate) = &props.validate {
+            validate.apply(&value)?;
         }
+
+        Ok(Value::String(value))
     }
 
     fn setup(props: &Self::Properties) -> ManagedFieldState {
@@ -370,7 +367,6 @@ impl ManagedField for StandardField {
             default,
             radio_group: false,
             unique: false,
-            submit_converter: None,
         }
     }
 
index 868b9ddb0aed9522e2c8c836da18163a51d81174..fe35d4d73a0d4887ea91f3ede58774e701a9fc13 100644 (file)
@@ -67,7 +67,6 @@ impl ManagedField for HiddenField {
             default,
             radio_group: false,
             unique: false,
-            submit_converter: None,
         }
     }
 
index d57da738422c27ea3d27a67abe2c42a2517d38fc..e1a083196dae575a684b28b576efaef4a4d413de 100644 (file)
@@ -5,7 +5,9 @@ use wasm_bindgen::{closure::Closure, JsCast};
 use yew::html::Scope;
 use yew::prelude::*;
 
-use super::{FieldHandle, FieldOptions, FormContext, FormContextObserver, ValidateFn};
+use super::{
+    FieldHandle, FieldOptions, FormContext, FormContextObserver, SubmitValidateFn,
+};
 use crate::props::FieldBuilder;
 
 /// Managed field state.
@@ -33,9 +35,6 @@ pub struct ManagedFieldState {
     ///
     /// Instead, use the same state for all of those fields.
     pub unique: bool,
-
-    /// Optional conversion called by [FormContext::get_submit_data]
-    pub submit_converter: Option<Callback<Value, Option<Value>>>,
 }
 
 /// Managed field context.
@@ -167,8 +166,9 @@ pub trait ManagedField: Sized {
     /// The validation function.
     ///
     /// Gets the result of [`validation_args`](Self::validation_args) and the value as parameter.
-    fn validator(_props: &Self::ValidateClosure, _value: &Value) -> Result<(), Error> {
-        Ok(())
+    /// If valid, it should return the value to be submitted.
+    fn validator(_props: &Self::ValidateClosure, value: &Value) -> Result<Value, Error> {
+        Ok(value.clone())
     }
 
     /// Returns the initial field setup.
@@ -229,7 +229,7 @@ pub struct ManagedFieldMaster<MF: ManagedField> {
     label_clicked_closure: Option<Closure<dyn Fn()>>,
 
     /// The validation function
-    validate: ValidateFn<Value>,
+    validate: SubmitValidateFn<Value>,
 }
 
 impl<MF: ManagedField + 'static> ManagedFieldMaster<MF> {
@@ -278,7 +278,6 @@ impl<MF: ManagedField + 'static> ManagedFieldMaster<MF> {
             Some(self.validate.clone()),
             options,
             self.comp_state.unique,
-            self.comp_state.submit_converter.clone(),
         );
 
         // FormContext may already have field data (i.e for unique fields), so sync back
@@ -299,11 +298,12 @@ impl<MF: ManagedField + 'static> Component for ManagedFieldMaster<MF> {
         let props = ctx.props();
 
         let validation_args = MF::validation_args(props);
-        let validate = ValidateFn::new(move |value| MF::validator(&validation_args, value));
+        let validate = SubmitValidateFn::new(move |value| MF::validator(&validation_args, value));
 
         let mut comp_state = MF::setup(props);
         comp_state.valid = validate
             .apply(&comp_state.value)
+            .map(|_| ())
             .map_err(|err| err.to_string());
 
         let sub_context = ManagedFieldContext::new(ctx, &comp_state);
@@ -354,8 +354,9 @@ impl<MF: ManagedField + 'static> Component for ManagedFieldMaster<MF> {
                 }
 
                 let value = value.unwrap_or(self.comp_state.value.clone());
-                let valid = valid
-                    .unwrap_or_else(|| self.validate.apply(&value).map_err(|e| e.to_string()));
+                let valid =
+                    valid.unwrap_or_else(|| self.validate.apply(&value)
+                        .map(|_| ()).map_err(|e| e.to_string()));
 
                 let value_changed = value != self.comp_state.value;
                 let valid_changed = valid != self.comp_state.valid;
@@ -370,7 +371,7 @@ impl<MF: ManagedField + 'static> Component for ManagedFieldMaster<MF> {
                 true
             }
             Msg::UpdateValue(value) => {
-                let valid = self.validate.apply(&value).map_err(|e| e.to_string());
+                let valid = self.validate.apply(&value).map(|_| ()).map_err(|e| e.to_string());
 
                 let value_changed = value != self.comp_state.value;
                 let valid_changed = valid != self.comp_state.valid;
@@ -395,6 +396,7 @@ impl<MF: ManagedField + 'static> Component for ManagedFieldMaster<MF> {
                     let valid = self
                         .validate
                         .apply(&self.comp_state.value)
+                        .map(|_| ())
                         .map_err(|e| e.to_string());
 
                     let valid_changed = valid != self.comp_state.valid;
@@ -477,7 +479,7 @@ impl<MF: ManagedField + 'static> Component for ManagedFieldMaster<MF> {
 
         if validation_args != old_validation_args {
             log::info!("UPDATE VF {:?}", props.as_input_props().name);
-            let validate = ValidateFn::new(move |value| MF::validator(&validation_args, value));
+            let validate = SubmitValidateFn::new(move |value| MF::validator(&validation_args, value));
             refresh1 = true;
             self.validate = validate.clone();
             if let Some(field_handle) = &mut self.field_handle {
index 241ba00a58e6092aae3786072c3cbef163a8a0f6..721018ac5f14f067ea42081f2712cff5cbb4b54e 100644 (file)
@@ -50,6 +50,9 @@ pub use submit_button::{PwtSubmitButton, SubmitButton};
 mod textarea;
 pub use textarea::{PwtTextArea, TextArea};
 
+mod submit_validate;
+pub use submit_validate::{IntoSubmitValidateFn, SubmitValidateFn};
+
 mod validate;
 pub use validate::{IntoValidateFn, ValidateFn};
 
index 2fefdbac4e3e0995f8fff91f697e6fd2b1e35daa..e074dc81d3774b29fb623ba86ca8b08d185514b1 100644 (file)
@@ -383,28 +383,6 @@ pub struct ValidateClosure<T> {
     validate: Option<ValidateFn<T>>,
 }
 
-impl<T: NumberTypeInfo> NumberField<T> {
-    // Note: This is called on submit, but only for valid fields
-    fn submit_convert(value: Value) -> Option<Value> {
-        match &value {
-            Value::Number(_) | Value::Null => Some(value),
-            Value::String(text) => {
-                if text.is_empty() {
-                    return Some(Value::Null);
-                }
-                match T::value_to_number(&value) {
-                    Ok(n) => Some(n.into()),
-                    Err(err) => {
-                        log::error!("NumberField: submit_convert failed - {err}");
-                        None
-                    }
-                }
-            }
-            _ => None,
-        }
-    }
-}
-
 impl<T: NumberTypeInfo> ManagedField for NumberField<T> {
     type Properties = Number<T>;
     type Message = Msg;
@@ -419,7 +397,7 @@ impl<T: NumberTypeInfo> ManagedField for NumberField<T> {
         }
     }
 
-    fn validator(props: &Self::ValidateClosure, value: &Value) -> Result<(), Error> {
+    fn validator(props: &Self::ValidateClosure, value: &Value) -> Result<Value, Error> {
         let is_empty = match value {
             Value::Null => true,
             Value::Number(_) => false,
@@ -431,7 +409,7 @@ impl<T: NumberTypeInfo> ManagedField for NumberField<T> {
             if props.required {
                 return Err(Error::msg(tr!("Field may not be empty.")));
             } else {
-                return Ok(());
+                return Ok(Value::Null);
             }
         }
 
@@ -457,10 +435,10 @@ impl<T: NumberTypeInfo> ManagedField for NumberField<T> {
             }
         }
 
-        match &props.validate {
-            Some(validate) => validate.apply(&number),
-            None => Ok(()),
+        if let Some(validate) = &props.validate {
+            validate.apply(&number)?;
         }
+        Ok(number.into())
     }
 
     fn setup(props: &Self::Properties) -> ManagedFieldState {
@@ -486,7 +464,6 @@ impl<T: NumberTypeInfo> ManagedField for NumberField<T> {
             default,
             radio_group: false,
             unique: false,
-            submit_converter: Some(Callback::from(Self::submit_convert)),
         }
     }
 
index c70187c71c03c03f4817b18e5cf083f42effc5d3..fbf62c18d184a87ee8ac17d99debbe3395f0b32c 100644 (file)
@@ -194,33 +194,30 @@ impl<S: DataStore + 'static> ManagedField for SelectorField<S> {
         }
     }
 
-    fn validator(props: &Self::ValidateClosure, value: &Value) -> Result<(), Error> {
+    fn validator(props: &Self::ValidateClosure, value: &Value) -> Result<Value, Error> {
         let value = match value {
             Value::Null => String::new(),
             Value::String(v) => v.clone(),
-            _ => {
-                // should not happen
-                log::error!("PwtField: got wrong data type in validate!");
-                String::new()
-            }
+            _ => return Err(Error::msg(tr!("got wrong data type."))),
         };
 
         if value.is_empty() {
             if props.required {
                 bail!("Field may not be empty.");
             } else {
-                return Ok(());
+                return Ok(Value::String(String::new()));
             }
         }
 
         if !props.store.is_empty() {
-            match &props.validate {
-                Some(validate) => validate.apply(&(value.into(), props.store.clone())),
-                None => Ok(()),
+            if let Some(validate) = &props.validate {
+                validate.apply(&(value.clone().into(), props.store.clone()))?;
             }
         } else {
             bail!("no data loaded");
         }
+
+        Ok(Value::String(value))
     }
 
     fn setup(props: &Self::Properties) -> ManagedFieldState {
@@ -233,7 +230,6 @@ impl<S: DataStore + 'static> ManagedField for SelectorField<S> {
             default,
             radio_group: false,
             unique: false,
-            submit_converter: None,
         }
     }
 
diff --git a/src/widget/form/submit_validate.rs b/src/widget/form/submit_validate.rs
new file mode 100644 (file)
index 0000000..e60be75
--- /dev/null
@@ -0,0 +1,62 @@
+use std::rc::Rc;
+
+use anyhow::Error;
+use serde_json::Value;
+
+/// A [SubmitValidateFn] function is a callback that detrermines if the
+/// passed record is valid, and return the value to be submitted.
+///
+/// Wraps `Rc` around `Fn` so it can be passed as a prop.
+pub struct SubmitValidateFn<T>(Rc<dyn Fn(&T) -> Result<Value, Error>>);
+
+impl<T> Clone for SubmitValidateFn<T> {
+    fn clone(&self) -> Self {
+        Self(Rc::clone(&self.0))
+    }
+}
+
+impl<T> PartialEq for SubmitValidateFn<T> {
+    fn eq(&self, other: &Self) -> bool {
+        Rc::ptr_eq(&self.0, &other.0)
+    }
+}
+
+impl<T> SubmitValidateFn<T> {
+    /// Creates a new [`SubmitValidateFn`]
+    pub fn new(validate: impl 'static + Fn(&T) -> Result<Value, Error>) -> Self {
+        Self(Rc::new(validate))
+    }
+
+    /// Apply the validation function
+    pub fn apply(&self, data: &T) -> Result<Value, Error> {
+        (self.0)(data)
+    }
+}
+
+impl<T> std::fmt::Debug for SubmitValidateFn<T> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "SubmitValidateFn({:p})", self.0)
+    }
+}
+
+impl<T, F: 'static + Fn(&T) -> Result<Value, Error>> From<F> for SubmitValidateFn<T> {
+    fn from(f: F) -> Self {
+        SubmitValidateFn::new(f)
+    }
+}
+
+/// Helper trait to create an optional [SubmitValidateFn] property.
+pub trait IntoSubmitValidateFn<T> {
+    fn into_submit_validate_fn(self) -> Option<SubmitValidateFn<T>>;
+}
+
+impl<T, V: Into<SubmitValidateFn<T>>> IntoSubmitValidateFn<T> for V {
+    fn into_submit_validate_fn(self) -> Option<SubmitValidateFn<T>> {
+        Some(self.into())
+    }
+}
+impl<T, V: Into<SubmitValidateFn<T>>> IntoSubmitValidateFn<T> for Option<V> {
+    fn into_submit_validate_fn(self) -> Option<SubmitValidateFn<T>> {
+        self.map(|v| v.into())
+    }
+}
index e89245ba33c3c75419169f13093dd2eb8562dc23..bf888bf1f5186ee5ac7dd72a9c890d9816cfeb69 100644 (file)
@@ -120,7 +120,7 @@ impl ManagedField for TextAreaField {
         }
     }
 
-    fn validator(props: &Self::ValidateClosure, value: &Value) -> Result<(), Error> {
+    fn validator(props: &Self::ValidateClosure, value: &Value) -> Result<Value, Error> {
         let value = match value {
             Value::Null => String::new(),
             Value::String(v) => v.clone(),
@@ -135,14 +135,15 @@ impl ManagedField for TextAreaField {
             if props.required {
                 return Err(Error::msg(tr!("Field may not be empty.")));
             } else {
-                return Ok(());
+                return Ok(Value::String(String::new()));
             }
         }
 
-        match &props.validate {
-            Some(validate) => validate.apply(&value),
-            None => Ok(()),
+        if let Some(validate) = &props.validate {
+            validate.apply(&value)?;
         }
+
+        Ok(Value::String(value))
     }
 
     fn setup(props: &Self::Properties) -> ManagedFieldState {
@@ -165,7 +166,6 @@ impl ManagedField for TextAreaField {
             default,
             radio_group: false,
             unique: false,
-            submit_converter: None,
         }
     }
 
index 717778b7fa0d42cbd30c3b304531360fb87cedb9..fe1b667a049dd7666e5b0cfe691200124b565d3c 100644 (file)
@@ -105,9 +105,9 @@ impl ManagedField for PwtTristateBoolean {
 
     fn validation_args(_props: &Self::Properties) -> Self::ValidateClosure {}
 
-    fn validator(_props: &Self::ValidateClosure, value: &Value) -> Result<(), Error> {
+    fn validator(_props: &Self::ValidateClosure, value: &Value) -> Result<Value, Error> {
         match value {
-            Value::Null | Value::Bool(_) => Ok(()),
+            Value::Null | Value::Bool(_) => Ok(value.clone()),
             _ => Err(Error::msg(tr!("Got wrong data type!"))),
         }
     }
@@ -136,7 +136,6 @@ impl ManagedField for PwtTristateBoolean {
             default,
             radio_group: false,
             unique: false,
-            submit_converter: None,
         }
     }
 
index 58c67720b44d766f30064fa9c4b319c041547929..42ef3f21d5c2dc00723d47fc3796b35e83a179de 100644 (file)
@@ -135,7 +135,6 @@ impl ManagedField for MenuCheckboxField {
             default: default.into(),
             radio_group: props.radio_group,
             unique: true,
-            submit_converter: None,
         }
     }