conditional validation

This commit is contained in:
Mitchell Marino 2024-06-18 14:18:36 -05:00
parent bbde4d6331
commit 89375a5b0c
4 changed files with 44 additions and 11 deletions

View File

@ -44,7 +44,7 @@ impl<FD: FormToolData> FormBuilder<FD> {
let cx = self.cx.clone();
let render_fn = move |fs: Rc<FD::Style>, fd: RwSignal<FD>| {
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) => {

View File

@ -19,7 +19,7 @@ pub mod text_area;
pub mod text_input;
pub trait BuilderFn<B, CX>: Fn(B, Rc<CX>) -> B {}
pub trait ValidationFn<FDT: ?Sized>: Fn(&FDT) -> Result<(), String> + 'static {}
pub trait ValidationFn<FD: ?Sized>: Fn(&FD) -> Result<(), String> + 'static {}
pub trait ValidationCb: Fn() -> bool + 'static {}
pub trait ParseFn<CR, FDT>: Fn(CR) -> Result<FDT, String> + 'static {}
pub trait UnparseFn<CR, FDT>: Fn(FDT) -> CR + 'static {}
@ -177,7 +177,7 @@ pub(crate) struct BuiltControlData<FD: FormToolData, C: ControlData, FDT> {
pub(crate) parse_fn: Box<dyn ParseFn<C::ReturnType, FDT>>,
pub(crate) unparse_fn: Box<dyn UnparseFn<C::ReturnType, FDT>>,
pub(crate) validation_fn: Option<Rc<dyn ValidationFn<FD>>>,
pub(crate) show_when: Option<Box<dyn ShowWhenFn<FD, FD::Context>>>,
pub(crate) show_when: Option<Rc<dyn ShowWhenFn<FD, FD::Context>>>,
}
/// A builder for a interactive control.
@ -188,7 +188,7 @@ pub struct ControlBuilder<FD: FormToolData, C: ControlData, FDT> {
pub(crate) unparse_fn: Option<Box<dyn UnparseFn<C::ReturnType, FDT>>>,
pub(crate) validation_fn: Option<Rc<dyn ValidationFn<FD>>>,
pub(crate) style_attributes: Vec<<FD::Style as FormStyle>::StylingAttributes>,
pub(crate) show_when: Option<Box<dyn ShowWhenFn<FD, FD::Context>>>,
pub(crate) show_when: Option<Rc<dyn ShowWhenFn<FD, FD::Context>>>,
pub data: C,
}
@ -249,7 +249,7 @@ impl<FD: FormToolData, C: ControlData, FDT> ControlBuilder<FD, C, FDT> {
mut self,
when: impl Fn(Signal<FD>, Rc<FD::Context>) -> bool + 'static,
) -> Self {
self.show_when = Some(Box::new(when));
self.show_when = Some(Rc::new(when));
self
}

View File

@ -33,7 +33,9 @@ impl<FD: FormToolData> FormValidator<FD> {
/// With this, you can render the form, get the form data, or get
/// a validator for the data.
pub struct Form<FD: FormToolData> {
/// The form data signal.
pub fd: RwSignal<FD>,
/// The list of validations
pub(crate) validations: Vec<Rc<dyn ValidationFn<FD>>>,
pub(crate) view: View,
}

View File

@ -125,8 +125,24 @@ impl<FD: FormToolData> FormBuilder<FD> {
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<FD: FormToolData> FormBuilder<FD> {
};
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);