generated from mitchell/rust_template
remove vanity builder and add docs
This commit is contained in:
parent
3621e5fb7b
commit
a3311b2b63
@ -1,7 +1,7 @@
|
|||||||
use std::{fmt::Display, rc::Rc, str::FromStr};
|
use std::{fmt::Display, rc::Rc, str::FromStr};
|
||||||
|
|
||||||
use crate::{form::FormToolData, styles::FormStyle};
|
use crate::{form::FormToolData, styles::FormStyle};
|
||||||
use leptos::{Signal, View};
|
use leptos::{RwSignal, Signal, View};
|
||||||
|
|
||||||
pub mod heading;
|
pub mod heading;
|
||||||
pub mod select;
|
pub mod select;
|
||||||
@ -15,6 +15,10 @@ pub trait ParseFn<CR, FDT>: Fn(CR) -> Result<FDT, String> + 'static {}
|
|||||||
pub trait UnparseFn<CR, FDT>: Fn(FDT) -> CR + 'static {}
|
pub trait UnparseFn<CR, FDT>: Fn(FDT) -> CR + 'static {}
|
||||||
pub trait FieldGetter<FD, FDT>: Fn(FD) -> FDT + 'static {}
|
pub trait FieldGetter<FD, FDT>: Fn(FD) -> FDT + 'static {}
|
||||||
pub trait FieldSetter<FD, FDT>: Fn(&mut FD, FDT) + 'static {}
|
pub trait FieldSetter<FD, FDT>: Fn(&mut FD, FDT) + 'static {}
|
||||||
|
pub trait RenderFn<FS, FD>:
|
||||||
|
FnOnce(&FS, RwSignal<FD>) -> (View, Option<Box<dyn ValidationCb>>) + 'static
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
// implement the traits for all valid types
|
// implement the traits for all valid types
|
||||||
impl<FDT, T> ValidationFn<FDT> for T where T: Fn(&FDT) -> Result<(), String> + 'static {}
|
impl<FDT, T> ValidationFn<FDT> for T where T: Fn(&FDT) -> Result<(), String> + 'static {}
|
||||||
@ -23,21 +27,22 @@ impl<CR, FDT, F> ParseFn<CR, FDT> for F where F: Fn(CR) -> Result<FDT, String> +
|
|||||||
impl<CR, FDT, F> UnparseFn<CR, FDT> for F where F: Fn(FDT) -> CR + 'static {}
|
impl<CR, FDT, F> UnparseFn<CR, FDT> for F where F: Fn(FDT) -> CR + 'static {}
|
||||||
impl<FD, FDT, F> FieldGetter<FD, FDT> for F where F: Fn(FD) -> FDT + 'static {}
|
impl<FD, FDT, F> FieldGetter<FD, FDT> for F where F: Fn(FD) -> FDT + 'static {}
|
||||||
impl<FD, FDT, F> FieldSetter<FD, FDT> for F where F: Fn(&mut FD, FDT) + 'static {}
|
impl<FD, FDT, F> FieldSetter<FD, FDT> for F where F: Fn(&mut FD, FDT) + 'static {}
|
||||||
|
impl<FS, FD, F> RenderFn<FS, FD> for F where
|
||||||
|
F: FnOnce(&FS, RwSignal<FD>) -> (View, Option<Box<dyn ValidationCb>>) + 'static
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait for the data needed to render an static control.
|
||||||
pub trait VanityControlData: 'static {
|
pub trait VanityControlData: 'static {
|
||||||
|
/// Builds the control, returning the [`View`] that was built.
|
||||||
fn build_control<FS: FormStyle>(fs: &FS, control: ControlRenderData<FS, Self>) -> View;
|
fn build_control<FS: FormStyle>(fs: &FS, control: ControlRenderData<FS, Self>) -> View;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: what if the `FS` parameter was extracted to the trait level.
|
/// A trait for the data needed to render an interactive control.
|
||||||
// Then this would be trait object able.
|
|
||||||
// If this is trait object able, then we can store this in a list,
|
|
||||||
// and differ rendering the control until we actually need to form view.
|
|
||||||
// Which, in turn, would get rid of the Form Builder as an enum (which was
|
|
||||||
// done to avoid rendering on the server).
|
|
||||||
pub trait ControlData: 'static {
|
pub trait ControlData: 'static {
|
||||||
type ReturnType: Clone;
|
type ReturnType: Clone;
|
||||||
|
|
||||||
// TODO: this should also return a getter for the data
|
/// Builds the control, returning the [`View`] that was built.
|
||||||
fn build_control<FS: FormStyle>(
|
fn build_control<FS: FormStyle>(
|
||||||
fs: &FS,
|
fs: &FS,
|
||||||
control: ControlRenderData<FS, Self>,
|
control: ControlRenderData<FS, Self>,
|
||||||
@ -47,17 +52,20 @@ pub trait ControlData: 'static {
|
|||||||
) -> View;
|
) -> View;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The data needed to render a interactive control of type `C`.
|
||||||
pub struct ControlRenderData<FS: FormStyle + ?Sized, C: ?Sized> {
|
pub struct ControlRenderData<FS: FormStyle + ?Sized, C: ?Sized> {
|
||||||
pub data: Box<C>,
|
pub data: Box<C>,
|
||||||
pub style: Vec<FS::StylingAttributes>,
|
pub style: Vec<FS::StylingAttributes>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The data needed to render a static control of type `C`.
|
||||||
pub struct VanityControlBuilder<FS: FormStyle, C: VanityControlData> {
|
pub struct VanityControlBuilder<FS: FormStyle, C: VanityControlData> {
|
||||||
pub(crate) style_attributes: Vec<FS::StylingAttributes>,
|
pub(crate) style_attributes: Vec<FS::StylingAttributes>,
|
||||||
pub(crate) data: C,
|
pub(crate) data: C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<FS: FormStyle, C: VanityControlData> VanityControlBuilder<FS, C> {
|
impl<FS: FormStyle, C: VanityControlData> VanityControlBuilder<FS, C> {
|
||||||
|
/// Creates a new [`VanityControlBuilder`] with the given [`VanityControlData`].
|
||||||
pub(crate) fn new(data: C) -> Self {
|
pub(crate) fn new(data: C) -> Self {
|
||||||
VanityControlBuilder {
|
VanityControlBuilder {
|
||||||
data,
|
data,
|
||||||
@ -65,6 +73,7 @@ impl<FS: FormStyle, C: VanityControlData> VanityControlBuilder<FS, C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builds the builder into the data needed to render the control.
|
||||||
pub(crate) fn build(self) -> ControlRenderData<FS, C> {
|
pub(crate) fn build(self) -> ControlRenderData<FS, C> {
|
||||||
ControlRenderData {
|
ControlRenderData {
|
||||||
data: Box::new(self.data),
|
data: Box::new(self.data),
|
||||||
@ -73,12 +82,16 @@ impl<FS: FormStyle, C: VanityControlData> VanityControlBuilder<FS, C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The possibilities for errors when building a control.
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||||
pub enum ControlBuildError {
|
pub enum ControlBuildError {
|
||||||
/// The field that this control belongs to is not specified.
|
/// The getter field was not specified.
|
||||||
MissingGetter,
|
MissingGetter,
|
||||||
|
/// The setter field was not specified.
|
||||||
MissingSetter,
|
MissingSetter,
|
||||||
|
/// The parse function was not specified.
|
||||||
MissingParseFn,
|
MissingParseFn,
|
||||||
|
/// The unparse function was not specified.
|
||||||
MissingUnParseFn,
|
MissingUnParseFn,
|
||||||
}
|
}
|
||||||
impl Display for ControlBuildError {
|
impl Display for ControlBuildError {
|
||||||
@ -93,6 +106,7 @@ impl Display for ControlBuildError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The data returned fomr a control's build function.
|
||||||
pub(crate) struct BuiltControlData<FD: FormToolData, FS: FormStyle, C: ControlData, FDT> {
|
pub(crate) struct BuiltControlData<FD: FormToolData, FS: FormStyle, C: ControlData, FDT> {
|
||||||
pub(crate) render_data: ControlRenderData<FS, C>,
|
pub(crate) render_data: ControlRenderData<FS, C>,
|
||||||
pub(crate) getter: Rc<dyn FieldGetter<FD, FDT>>,
|
pub(crate) getter: Rc<dyn FieldGetter<FD, FDT>>,
|
||||||
@ -102,6 +116,7 @@ pub(crate) struct BuiltControlData<FD: FormToolData, FS: FormStyle, C: ControlDa
|
|||||||
pub(crate) validation_fn: Option<Rc<dyn ValidationFn<FD>>>,
|
pub(crate) validation_fn: Option<Rc<dyn ValidationFn<FD>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A builder for a interactive control.
|
||||||
pub struct ControlBuilder<FD: FormToolData, FS: FormStyle, C: ControlData, FDT> {
|
pub struct ControlBuilder<FD: FormToolData, FS: FormStyle, C: ControlData, FDT> {
|
||||||
pub(crate) getter: Option<Rc<dyn FieldGetter<FD, FDT>>>,
|
pub(crate) getter: Option<Rc<dyn FieldGetter<FD, FDT>>>,
|
||||||
pub(crate) setter: Option<Rc<dyn FieldSetter<FD, FDT>>>,
|
pub(crate) setter: Option<Rc<dyn FieldSetter<FD, FDT>>>,
|
||||||
@ -113,6 +128,7 @@ pub struct ControlBuilder<FD: FormToolData, FS: FormStyle, C: ControlData, FDT>
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<FD: FormToolData, FS: FormStyle, C: ControlData, FDT> ControlBuilder<FD, FS, C, FDT> {
|
impl<FD: FormToolData, FS: FormStyle, C: ControlData, FDT> ControlBuilder<FD, FS, C, FDT> {
|
||||||
|
/// Creates a new [`ControlBuilder`] with the given [`ControlData`].
|
||||||
pub(crate) fn new(data: C) -> Self {
|
pub(crate) fn new(data: C) -> Self {
|
||||||
ControlBuilder {
|
ControlBuilder {
|
||||||
data,
|
data,
|
||||||
@ -125,6 +141,9 @@ impl<FD: FormToolData, FS: FormStyle, C: ControlData, FDT> ControlBuilder<FD, FS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builds the builder into the data needed to render the control.
|
||||||
|
///
|
||||||
|
/// This fails if a required field was not specified.
|
||||||
pub(crate) fn build(self) -> Result<BuiltControlData<FD, FS, C, FDT>, ControlBuildError> {
|
pub(crate) fn build(self) -> Result<BuiltControlData<FD, FS, C, FDT>, ControlBuildError> {
|
||||||
let getter = match self.getter {
|
let getter = match self.getter {
|
||||||
Some(getter) => getter,
|
Some(getter) => getter,
|
||||||
|
|||||||
183
src/form.rs
183
src/form.rs
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
controls::{
|
controls::{
|
||||||
BuiltControlData, ControlBuilder, ControlData, FieldGetter, FieldSetter, ParseFn,
|
BuiltControlData, ControlBuilder, ControlData, FieldGetter, FieldSetter, ParseFn, RenderFn,
|
||||||
UnparseFn, ValidationCb, ValidationFn, VanityControlBuilder, VanityControlData,
|
UnparseFn, ValidationCb, ValidationFn, VanityControlBuilder, VanityControlData,
|
||||||
},
|
},
|
||||||
styles::FormStyle,
|
styles::FormStyle,
|
||||||
@ -53,17 +53,17 @@ impl<FD: FormToolData> Form<FD> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate(&self, form_data: &FD) -> Result<(), String> {
|
/// Validates the [`ToolFormData`], returning the result
|
||||||
for v in self.validations.iter() {
|
pub fn validate(&self) -> Result<(), String> {
|
||||||
(*v)(form_data)?;
|
self.fd.get_untracked().validate()
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the view associated with this [`Form`].
|
||||||
pub fn view(&self) -> View {
|
pub fn view(&self) -> View {
|
||||||
self.view.clone()
|
self.view.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Splits this [`Form`] into it's parts.
|
||||||
pub fn to_parts(self) -> (RwSignal<FD>, Validator<FD>, View) {
|
pub fn to_parts(self) -> (RwSignal<FD>, Validator<FD>, View) {
|
||||||
(
|
(
|
||||||
self.fd,
|
self.fd,
|
||||||
@ -81,66 +81,29 @@ impl<FD: FormToolData> IntoView for Form<FD> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A version of the [`FormBuilder`] that contains all the data
|
|
||||||
/// needed for full building of a [`Form`].
|
|
||||||
struct FullFormBuilder<FD: FormToolData, FS: FormStyle> {
|
|
||||||
fd: RwSignal<FD>,
|
|
||||||
fs: FS,
|
|
||||||
validations: Vec<Rc<dyn ValidationFn<FD>>>,
|
|
||||||
render_fns:
|
|
||||||
Vec<Box<dyn FnOnce(&FS, RwSignal<FD>) -> (View, Option<Box<dyn ValidationCb>>) + 'static>>,
|
|
||||||
// validation_cbs: Vec<Box<dyn ValidationCb>>,
|
|
||||||
// views: Vec<View>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The internal type for building forms
|
|
||||||
///
|
|
||||||
/// This allows us to build either the full form
|
|
||||||
/// with views, validation and data. Or we can just
|
|
||||||
/// build the validation functions.
|
|
||||||
///
|
|
||||||
/// This is useful in the context of a server that
|
|
||||||
/// cannot or should not render the form. You can
|
|
||||||
/// still get all the validation functions from the
|
|
||||||
/// form data.
|
|
||||||
enum FormBuilderInner<FD: FormToolData, FS: FormStyle> {
|
|
||||||
/// For building the form with views
|
|
||||||
FullBuilder(FullFormBuilder<FD, FS>),
|
|
||||||
/// For building only the validations for the form
|
|
||||||
ValidationBuilder {
|
|
||||||
validations: Vec<Rc<dyn ValidationFn<FD>>>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A builder for laying out forms.
|
/// A builder for laying out forms.
|
||||||
///
|
///
|
||||||
/// This builder allows you to specify what component should make up the form.
|
/// This builder allows you to specify what components should make up the form.
|
||||||
pub struct FormBuilder<FD: FormToolData, FS: FormStyle> {
|
pub struct FormBuilder<FD: FormToolData, FS: FormStyle> {
|
||||||
inner: FormBuilderInner<FD, FS>,
|
/// The [`ToolFormData`] signal.
|
||||||
|
fd: RwSignal<FD>,
|
||||||
|
/// The [`FormStyle`].
|
||||||
|
fs: FS,
|
||||||
|
/// The list of [`ValidationFn`]s.
|
||||||
|
validations: Vec<Rc<dyn ValidationFn<FD>>>,
|
||||||
|
/// The list of functions that will render the form.
|
||||||
|
render_fns: Vec<Box<dyn RenderFn<FS, FD>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
||||||
/// Creates a new full builder.
|
/// Creates a new [`FormBuilder`]
|
||||||
fn new_full_builder(starting_data: FD, form_style: FS) -> FormBuilder<FD, FS> {
|
fn new(starting_data: FD, form_style: FS) -> FormBuilder<FD, FS> {
|
||||||
let fd = create_rw_signal(starting_data);
|
let fd = create_rw_signal(starting_data);
|
||||||
FormBuilder {
|
FormBuilder {
|
||||||
inner: FormBuilderInner::FullBuilder(FullFormBuilder {
|
|
||||||
fd,
|
fd,
|
||||||
fs: form_style,
|
fs: form_style,
|
||||||
validations: Vec::new(),
|
validations: Vec::new(),
|
||||||
render_fns: Vec::new(),
|
render_fns: Vec::new(),
|
||||||
// validation_cbs: Vec::new(),
|
|
||||||
// views: Vec::new(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new builder that only collects the validation functions.
|
|
||||||
fn new_validation_builder() -> FormBuilder<FD, FS> {
|
|
||||||
FormBuilder {
|
|
||||||
inner: FormBuilderInner::ValidationBuilder {
|
|
||||||
validations: Vec::new(),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,18 +128,14 @@ impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn add_vanity<C: VanityControlData>(&mut self, vanity_control: VanityControlBuilder<FS, C>) {
|
fn add_vanity<C: VanityControlData>(&mut self, vanity_control: VanityControlBuilder<FS, C>) {
|
||||||
let builder = match &mut self.inner {
|
|
||||||
FormBuilderInner::FullBuilder(fb) => fb,
|
|
||||||
FormBuilderInner::ValidationBuilder { validations: _ } => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
let render_data = vanity_control.build();
|
let render_data = vanity_control.build();
|
||||||
|
|
||||||
let render_fn = move |fs: &FS, _| {
|
let render_fn = move |fs: &FS, _| {
|
||||||
let view = VanityControlData::build_control(fs, render_data);
|
let view = VanityControlData::build_control(fs, render_data);
|
||||||
(view, None)
|
(view, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
builder.render_fns.push(Box::new(render_fn));
|
self.render_fns.push(Box::new(render_fn));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_control<C: ControlData, FDT: Clone + PartialEq + 'static>(
|
fn add_control<C: ControlData, FDT: Clone + PartialEq + 'static>(
|
||||||
@ -195,19 +154,8 @@ impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
|||||||
Err(e) => panic!("Invalid Component: {}", e),
|
Err(e) => panic!("Invalid Component: {}", e),
|
||||||
};
|
};
|
||||||
|
|
||||||
let builder = match &mut self.inner {
|
|
||||||
FormBuilderInner::FullBuilder(fb) => fb,
|
|
||||||
FormBuilderInner::ValidationBuilder {
|
|
||||||
ref mut validations,
|
|
||||||
} => {
|
|
||||||
if let Some(validation_fn) = validation_fn {
|
|
||||||
validations.push(validation_fn);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Some(ref validation_fn) = validation_fn {
|
if let Some(ref validation_fn) = validation_fn {
|
||||||
builder.validations.push(validation_fn.clone());
|
self.validations.push(validation_fn.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let render_fn = move |fs: &FS, fd: RwSignal<FD>| {
|
let render_fn = move |fs: &FS, fd: RwSignal<FD>| {
|
||||||
@ -224,9 +172,7 @@ impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
|||||||
(view, Some(cb))
|
(view, Some(cb))
|
||||||
};
|
};
|
||||||
|
|
||||||
// builder.views.push(view);
|
self.render_fns.push(Box::new(render_fn));
|
||||||
// builder.validation_cbs.push(validation_cb);
|
|
||||||
builder.render_fns.push(Box::new(render_fn));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_control_view<C: ControlData, FDT: Clone + PartialEq + 'static>(
|
fn build_control_view<C: ControlData, FDT: Clone + PartialEq + 'static>(
|
||||||
@ -299,24 +245,19 @@ impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
|||||||
fn build_action_form<ServFn>(
|
fn build_action_form<ServFn>(
|
||||||
self,
|
self,
|
||||||
action: Action<ServFn, Result<ServFn::Output, ServerFnError<ServFn::Error>>>,
|
action: Action<ServFn, Result<ServFn::Output, ServerFnError<ServFn::Error>>>,
|
||||||
) -> Option<Form<FD>>
|
) -> Form<FD>
|
||||||
where
|
where
|
||||||
ServFn: DeserializeOwned + ServerFn<InputEncoding = PostUrl> + 'static,
|
ServFn: DeserializeOwned + ServerFn<InputEncoding = PostUrl> + 'static,
|
||||||
<<ServFn::Client as Client<ServFn::Error>>::Request as ClientReq<ServFn::Error>>::FormData:
|
<<ServFn::Client as Client<ServFn::Error>>::Request as ClientReq<ServFn::Error>>::FormData:
|
||||||
From<FormData>,
|
From<FormData>,
|
||||||
{
|
{
|
||||||
let builder = match self.inner {
|
let (views, validation_cbs): (Vec<_>, Vec<_>) = self
|
||||||
FormBuilderInner::FullBuilder(fb) => fb,
|
|
||||||
FormBuilderInner::ValidationBuilder { validations: _ } => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let (views, validation_cbs): (Vec<_>, Vec<_>) = builder
|
|
||||||
.render_fns
|
.render_fns
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|r_fn| r_fn(&builder.fs, builder.fd))
|
.map(|r_fn| r_fn(&self.fs, self.fd))
|
||||||
.unzip();
|
.unzip();
|
||||||
|
|
||||||
let elements = builder.fs.form_frame(views.into_view());
|
let elements = self.fs.form_frame(views.into_view());
|
||||||
|
|
||||||
let on_submit = move |ev: SubmitEvent| {
|
let on_submit = move |ev: SubmitEvent| {
|
||||||
let mut failed = false;
|
let mut failed = false;
|
||||||
@ -336,26 +277,21 @@ impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
|||||||
</ActionForm>
|
</ActionForm>
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(Form {
|
Form {
|
||||||
fd: builder.fd,
|
fd: self.fd,
|
||||||
validations: builder.validations,
|
validations: self.validations,
|
||||||
view,
|
view,
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_plain_form(self, url: String) -> Option<Form<FD>> {
|
fn build_plain_form(self, url: String) -> Form<FD> {
|
||||||
let builder = match self.inner {
|
let (views, validation_cbs): (Vec<_>, Vec<_>) = self
|
||||||
FormBuilderInner::FullBuilder(fb) => fb,
|
|
||||||
FormBuilderInner::ValidationBuilder { validations: _ } => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let (views, validation_cbs): (Vec<_>, Vec<_>) = builder
|
|
||||||
.render_fns
|
.render_fns
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|r_fn| r_fn(&builder.fs, builder.fd))
|
.map(|r_fn| r_fn(&self.fs, self.fd))
|
||||||
.unzip();
|
.unzip();
|
||||||
|
|
||||||
let elements = builder.fs.form_frame(views.into_view());
|
let elements = self.fs.form_frame(views.into_view());
|
||||||
|
|
||||||
let on_submit = move |ev: SubmitEvent| {
|
let on_submit = move |ev: SubmitEvent| {
|
||||||
let mut failed = false;
|
let mut failed = false;
|
||||||
@ -375,19 +311,17 @@ impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
|||||||
</Form>
|
</Form>
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(Form {
|
Form {
|
||||||
fd: builder.fd,
|
fd: self.fd,
|
||||||
validations: builder.validations,
|
validations: self.validations,
|
||||||
view,
|
view,
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validator(&self) -> Validator<FD> {
|
fn validator(&self) -> Validator<FD> {
|
||||||
let validations = match &self.inner {
|
Validator {
|
||||||
FormBuilderInner::FullBuilder(fb) => fb.validations.clone(),
|
validations: self.validations.clone(),
|
||||||
FormBuilderInner::ValidationBuilder { validations } => validations.clone(),
|
}
|
||||||
};
|
|
||||||
Validator { validations }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,15 +343,22 @@ pub trait FormToolData: Default + Clone + 'static {
|
|||||||
|
|
||||||
/// Constructs a [`Form`] for this [`FormToolData`] type.
|
/// Constructs a [`Form`] for this [`FormToolData`] type.
|
||||||
///
|
///
|
||||||
/// The [`Form`] provides the way to render the form.
|
/// This renders the form as a the leptos_router
|
||||||
|
/// [`Form`](leptos_router::Form)
|
||||||
|
/// component. Call [`get_action_form`]\() to get the
|
||||||
|
/// [`ActionForm`](leptos_router::ActionForm) version.
|
||||||
fn get_form(self, action: impl ToString, style: Self::Style) -> Form<Self> {
|
fn get_form(self, action: impl ToString, style: Self::Style) -> Form<Self> {
|
||||||
let builder = FormBuilder::new_full_builder(self, style);
|
let builder = FormBuilder::new(self, style);
|
||||||
let builder = Self::build_form(builder);
|
let builder = Self::build_form(builder);
|
||||||
builder
|
builder.build_plain_form(action.to_string())
|
||||||
.build_plain_form(action.to_string())
|
|
||||||
.expect("builder should be full builder")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructs a [`Form`] for this [`FormToolData`] type.
|
||||||
|
///
|
||||||
|
/// This renders the form as a the leptos_router
|
||||||
|
/// [`ActionForm`](leptos_router::ActionForm)
|
||||||
|
/// component. Call [`get_form`]\() to get the plain
|
||||||
|
/// [`Form`](leptos_router::Form) version.
|
||||||
fn get_action_form<ServFn>(
|
fn get_action_form<ServFn>(
|
||||||
self,
|
self,
|
||||||
action: Action<ServFn, Result<ServFn::Output, ServerFnError<ServFn::Error>>>,
|
action: Action<ServFn, Result<ServFn::Output, ServerFnError<ServFn::Error>>>,
|
||||||
@ -428,19 +369,29 @@ pub trait FormToolData: Default + Clone + 'static {
|
|||||||
<<ServFn::Client as Client<ServFn::Error>>::Request as ClientReq<ServFn::Error>>::FormData:
|
<<ServFn::Client as Client<ServFn::Error>>::Request as ClientReq<ServFn::Error>>::FormData:
|
||||||
From<FormData>,
|
From<FormData>,
|
||||||
{
|
{
|
||||||
let builder = FormBuilder::new_full_builder(self, style);
|
let builder = FormBuilder::new(self, style);
|
||||||
let builder = Self::build_form(builder);
|
let builder = Self::build_form(builder);
|
||||||
builder
|
builder.build_action_form(action)
|
||||||
.build_action_form(action)
|
|
||||||
.expect("builder should be full builder")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets a [`Validator`] for this [`ToolFormData`].
|
||||||
|
///
|
||||||
|
/// This doesn't render the view, but just collects all the validation
|
||||||
|
/// Functions from building the form. That means it can be called on the
|
||||||
|
/// Server and no rendering will be done.
|
||||||
|
///
|
||||||
|
/// However, the code to render the views are not configured out, it
|
||||||
|
/// simply doesn't run, so the view needs to compile even on the server.
|
||||||
fn get_validator() -> Validator<Self> {
|
fn get_validator() -> Validator<Self> {
|
||||||
let builder = FormBuilder::new_validation_builder();
|
let builder = FormBuilder::new(Self::default(), Self::Style::default());
|
||||||
let builder = Self::build_form(builder);
|
let builder = Self::build_form(builder);
|
||||||
builder.validator()
|
builder.validator()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validates this [`FormToolData`] struct.
|
||||||
|
///
|
||||||
|
/// This is shorthand for creating a validator with [`get_validator`]\()
|
||||||
|
/// and then calling `validator.validate(&self)`.
|
||||||
fn validate(&self) -> Result<(), String> {
|
fn validate(&self) -> Result<(), String> {
|
||||||
let validator = Self::get_validator();
|
let validator = Self::get_validator();
|
||||||
validator.validate(self)
|
validator.validate(self)
|
||||||
|
|||||||
Reference in New Issue
Block a user