From 89375a5b0c69ba6982ee0ed6968db5cacad27db8 Mon Sep 17 00:00:00 2001 From: Mitchell M Date: Tue, 18 Jun 2024 14:18:36 -0500 Subject: [PATCH] conditional validation --- src/controls/button.rs | 2 +- src/controls/mod.rs | 8 ++++---- src/form.rs | 2 ++ src/form_builder.rs | 43 ++++++++++++++++++++++++++++++++++++------ 4 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/controls/button.rs b/src/controls/button.rs index cd56d7c..cc317fa 100644 --- a/src/controls/button.rs +++ b/src/controls/button.rs @@ -44,7 +44,7 @@ impl FormBuilder { let cx = self.cx.clone(); let render_fn = move |fs: Rc, fd: RwSignal| { let render_data = Rc::new(render_data); - // let cloned_fs = fs.clone(); + let view = move || fs.clone().button(render_data.clone(), fd); let view = match show_when { Some(when) => { diff --git a/src/controls/mod.rs b/src/controls/mod.rs index f01d62f..544cb3c 100644 --- a/src/controls/mod.rs +++ b/src/controls/mod.rs @@ -19,7 +19,7 @@ pub mod text_area; pub mod text_input; pub trait BuilderFn: Fn(B, Rc) -> B {} -pub trait ValidationFn: Fn(&FDT) -> Result<(), String> + 'static {} +pub trait ValidationFn: Fn(&FD) -> Result<(), String> + 'static {} pub trait ValidationCb: Fn() -> bool + 'static {} pub trait ParseFn: Fn(CR) -> Result + 'static {} pub trait UnparseFn: Fn(FDT) -> CR + 'static {} @@ -177,7 +177,7 @@ pub(crate) struct BuiltControlData { pub(crate) parse_fn: Box>, pub(crate) unparse_fn: Box>, pub(crate) validation_fn: Option>>, - pub(crate) show_when: Option>>, + pub(crate) show_when: Option>>, } /// A builder for a interactive control. @@ -188,7 +188,7 @@ pub struct ControlBuilder { pub(crate) unparse_fn: Option>>, pub(crate) validation_fn: Option>>, pub(crate) style_attributes: Vec<::StylingAttributes>, - pub(crate) show_when: Option>>, + pub(crate) show_when: Option>>, pub data: C, } @@ -249,7 +249,7 @@ impl ControlBuilder { mut self, when: impl Fn(Signal, Rc) -> bool + 'static, ) -> Self { - self.show_when = Some(Box::new(when)); + self.show_when = Some(Rc::new(when)); self } diff --git a/src/form.rs b/src/form.rs index b8055a7..5e6557a 100644 --- a/src/form.rs +++ b/src/form.rs @@ -33,7 +33,9 @@ impl FormValidator { /// With this, you can render the form, get the form data, or get /// a validator for the data. pub struct Form { + /// The form data signal. pub fd: RwSignal, + /// The list of validations pub(crate) validations: Vec>>, pub(crate) view: View, } diff --git a/src/form_builder.rs b/src/form_builder.rs index 6d2fd47..3ce29d2 100644 --- a/src/form_builder.rs +++ b/src/form_builder.rs @@ -125,8 +125,24 @@ impl FormBuilder { Err(e) => panic!("Invalid Component: {}", e), }; - if let Some(ref validation_fn) = built_control_data.validation_fn { - self.validations.push(validation_fn.clone()); + if let Some(validation_fn) = built_control_data.validation_fn.clone() { + let validation_fn = if let Some(show_when) = built_control_data.show_when.clone() { + // we want the validation function to always succeed for hidden components + // thus, we need to modify the validation function + let cx = self.cx.clone(); + let new_validation_fn = move |fd: &FD| { + let (fd_signal, _) = create_signal(fd.clone()); + if !show_when(fd_signal.into(), cx.clone()) { + return Ok(()); + } + validation_fn(fd) + }; + Rc::new(new_validation_fn) + } else { + validation_fn + }; + + self.validations.push(validation_fn); } let cx = self.cx.clone(); @@ -174,14 +190,29 @@ impl FormBuilder { }; let value_getter = value_getter.into_signal(); + let cloned_show_when = show_when.clone(); + let cloned_cx = cx.clone(); let validation_cb = move || { - let validation_fn = validation_fn.as_ref(); + // first check if the validation signal is an error so that we + // can fail on parsing issues too + if let Some(Err(_)) = validation_signal.try_get_untracked() { + return false; + } + + // validation for non-visible fields always succeeds + if let Some(ref show_when) = cloned_show_when { + if !show_when(fd.into(), cloned_cx.clone()) { + return true; + } + } + + // run the validation function on the value now let validation_fn = match validation_fn { - Some(v) => v, - None => return true, // No validation function, so validation passes + Some(ref v) => v, + None => return true, // No validation function so validation passes }; - let data = fd.get(); + let data = fd.get_untracked(); let validation_result = validation_fn(&data); let succeeded = validation_result.is_ok(); validation_signal_set.set(validation_result);