Continue validation feature

This commit is contained in:
Thomas Gideon 2021-11-07 11:54:06 -05:00
parent bff36b0ce3
commit 0ff7d59f63
6 changed files with 116 additions and 24 deletions

View file

@ -21,7 +21,49 @@ impl Component for FormGroup {
render_if_ne(&mut self.props, props) render_if_ne(&mut self.props, props)
} }
#[cfg(not(feature = "validate"))]
fn view(&self) -> Html { fn view(&self) -> Html {
render::render_with_prefix(&self.props, "form-group", render::div(&self.props.children)) render::render_with_prefix(&self.props, "form-group", render::div(&self.props.children))
} }
} }
pub struct Form {
pub props: Props,
}
impl Component for Form {
type Message = ();
type Properties = Props;
fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
Self { props }
}
fn update(&mut self, _: Self::Message) -> ShouldRender {
false
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
render_if_ne(&mut self.props, props)
}
#[cfg(not(feature = "validate"))]
fn view(&self) -> Html {
let form = html! {
<form>
{ for self.props.children.iter() }
</form>
};
render::render_with_prefix(&self.props, "form", form)
}
#[cfg(feature = "validate")]
fn view(&self) -> Html {
let form = html! {
<form>
{ for self.props.children.iter() }
</form>
};
render::render_with_prefix(&self.props, "form", form)
}
}

View file

@ -21,6 +21,7 @@ impl Component for InputGroup {
render_if_ne(&mut self.props, props) render_if_ne(&mut self.props, props)
} }
#[cfg(not(feature = "validate"))]
fn view(&self) -> Html { fn view(&self) -> Html {
render::render_with_prefix( render::render_with_prefix(
&self.props, &self.props,
@ -28,4 +29,15 @@ impl Component for InputGroup {
render::div(&self.props.children), render::div(&self.props.children),
) )
} }
#[cfg(feature = "validate")]
fn view(&self) -> Html {
let div = html! {
<div>
{ for (&self.props.children).iter() }
{ super::render_validation_feedback(&self.props.error_code, &self.props.errors) }
</div>
};
render::render_with_prefix(&self.props, "input-group", div)
}
} }

View file

@ -66,6 +66,7 @@ impl Component for Input {
render_if_ne(&mut self.props, props) render_if_ne(&mut self.props, props)
} }
#[cfg(not(feature = "validate"))]
fn view(&self) -> Html { fn view(&self) -> Html {
let input_type = self.props.input_type.as_ref().unwrap_or(&InputType::Text); let input_type = self.props.input_type.as_ref().unwrap_or(&InputType::Text);
let mut prefix = if self.props.readonly { let mut prefix = if self.props.readonly {
@ -84,11 +85,40 @@ impl Component for Input {
}; };
render::render_with_prefix(&self.props, prefix, html) render::render_with_prefix(&self.props, prefix, html)
} }
#[cfg(feature = "validate")]
fn view(&self) -> Html {
let input_type = self.props.input_type.as_ref().unwrap_or(&InputType::Text);
let mut prefix = if self.props.readonly {
vec!["form-control-plaintext"]
} else {
vec!["form-control"]
};
prefix.push(valid_as_class(&self.props.valid));
let html = html! {
<>
<input
type=input_type
value=&self.state
readonly=self.props.readonly
onchange=self.link.callback(|evt| InputChange(evt))
/>
{ crate::input::render_validation_feedback_from_props(&self.props) }
</>
};
render::render_with_prefix(&self.props, prefix, html)
}
} }
#[cfg(feature = "validate")] #[cfg(feature = "validate")]
fn render_validation_feedback<S: AsRef<str>>(field: S, errors: &Option<ValidationErrors>) -> Html { pub fn render_validation_feedback(
if let Some(ref errors) = errors { error_key: &Option<String>,
errors: &Option<ValidationErrors>,
) -> Html {
// TODO keep chipping away
if error_key.is_none {
html! {}
} else if let Some(ref errors) = errors {
let errors = errors.field_errors(); let errors = errors.field_errors();
if let Some(errors) = errors.get(field.as_ref()) { if let Some(errors) = errors.get(field.as_ref()) {
html! { html! {
@ -103,3 +133,12 @@ fn render_validation_feedback<S: AsRef<str>>(field: S, errors: &Option<Validatio
html! {} html! {}
} }
} }
#[cfg(feature = "validate")]
fn render_validation_feedback_from_props(props: &Props) -> Html {
if let Some(error_code) = props.error_code.as_ref() {
render_validation_feedback(error_code, &props.errors)
} else {
html! {}
}
}

View file

@ -4,6 +4,8 @@ use crate::{
props::{add_opt_attr, collect_props, BootstrapProps}, props::{add_opt_attr, collect_props, BootstrapProps},
}; };
use std::collections::HashMap; use std::collections::HashMap;
#[cfg(feature = "validate")]
use validator::ValidationErrors;
use yew::{html::Children, prelude::*}; use yew::{html::Children, prelude::*};
#[derive(Properties, Clone, PartialEq)] #[derive(Properties, Clone, PartialEq)]
@ -22,6 +24,14 @@ pub struct Props {
#[prop_or_default] #[prop_or_default]
pub input_type: Option<InputType>, pub input_type: Option<InputType>,
// optional validation support
#[cfg(feature = "validate")]
#[prop_or_default]
pub error_code: Option<String>,
#[cfg(feature = "validate")]
#[prop_or_default]
pub errors: Option<ValidationErrors>,
// bootstrap specific // bootstrap specific
#[prop_or_default] #[prop_or_default]
pub border: Option<Border>, pub border: Option<Border>,

View file

@ -1,15 +1,11 @@
use super::props::Props; use super::props::Props;
use crate::{prelude::*, render}; use crate::{prelude::*, render};
#[cfg(feature = "validate")]
use validator::ValidationErrors;
use yew::{prelude::*, virtual_dom::VNode}; use yew::{prelude::*, virtual_dom::VNode};
pub struct TextArea { pub struct TextArea {
link: ComponentLink<Self>, link: ComponentLink<Self>,
state: String, state: String,
props: Props, props: Props,
#[cfg(feature = "validator")]
errors: Option<ValidationErrors>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -19,23 +15,11 @@ impl Component for TextArea {
type Message = InputChange; type Message = InputChange;
type Properties = Props; type Properties = Props;
#[cfg(not(feature = "validate"))]
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self { fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
let state = extract_state(&props); let state = extract_state(&props);
Self { props, state, link } Self { props, state, link }
} }
#[cfg(feature = "validate")]
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
let state = extract_state(&props);
Self {
props,
state,
link,
errors: None,
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender { fn update(&mut self, msg: Self::Message) -> ShouldRender {
if let InputChange(ChangeData::Value(value)) = msg { if let InputChange(ChangeData::Value(value)) = msg {
self.state = value.clone(); self.state = value.clone();
@ -73,21 +57,16 @@ impl Component for TextArea {
#[cfg(feature = "validate")] #[cfg(feature = "validate")]
fn view(&self) -> Html { fn view(&self) -> Html {
let prefix = vec!["form-control", &valid_as_class(&self.props.valid)]; let prefix = vec!["form-control", &valid_as_class(&self.props.valid)];
let feedback = if let Some(name) = self.props.name.as_ref() {
super::render_validation_feedback(name, &self.errors)
} else {
html! {}
};
let html = { let html = {
html! { html! {
<> <>
{ feedback }
<textarea <textarea
readonly=self.props.readonly readonly=self.props.readonly
onchange=self.link.callback(|evt| InputChange(evt)) onchange=self.link.callback(|evt| InputChange(evt))
> >
{ &self.state } { &self.state }
</textarea> </textarea>
{ super::render_validation_feedback_from_props(&self.props) }
</> </>
} }
}; };

View file

@ -1,5 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use std::collections::HashMap; use std::collections::HashMap;
#[cfg(feature = "validate")]
use validator::ValidationErrors;
use yew::{prelude::*, virtual_dom::VNode}; use yew::{prelude::*, virtual_dom::VNode};
pub(crate) trait IntoBsClass { pub(crate) trait IntoBsClass {
@ -32,6 +34,14 @@ pub struct Props {
#[prop_or_default] #[prop_or_default]
pub children: Children, pub children: Children,
// optional validation support
#[cfg(feature = "validate")]
#[prop_or_default]
pub error_code: Option<String>,
#[cfg(feature = "validate")]
#[prop_or_default]
pub errors: Option<ValidationErrors>,
// bootstrap specific // bootstrap specific
#[prop_or_default] #[prop_or_default]
pub border: Option<Border>, pub border: Option<Border>,