Continue validation feature
This commit is contained in:
parent
bff36b0ce3
commit
0ff7d59f63
6 changed files with 116 additions and 24 deletions
|
@ -21,7 +21,49 @@ impl Component for FormGroup {
|
|||
render_if_ne(&mut self.props, props)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "validate"))]
|
||||
fn view(&self) -> Html {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ impl Component for InputGroup {
|
|||
render_if_ne(&mut self.props, props)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "validate"))]
|
||||
fn view(&self) -> Html {
|
||||
render::render_with_prefix(
|
||||
&self.props,
|
||||
|
@ -28,4 +29,15 @@ impl Component for InputGroup {
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ impl Component for Input {
|
|||
render_if_ne(&mut self.props, props)
|
||||
}
|
||||
|
||||
#[cfg(not(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 {
|
||||
|
@ -84,11 +85,40 @@ impl Component for Input {
|
|||
};
|
||||
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")]
|
||||
fn render_validation_feedback<S: AsRef<str>>(field: S, errors: &Option<ValidationErrors>) -> Html {
|
||||
if let Some(ref errors) = errors {
|
||||
pub fn render_validation_feedback(
|
||||
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();
|
||||
if let Some(errors) = errors.get(field.as_ref()) {
|
||||
html! {
|
||||
|
@ -103,3 +133,12 @@ fn render_validation_feedback<S: AsRef<str>>(field: S, errors: &Option<Validatio
|
|||
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! {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ use crate::{
|
|||
props::{add_opt_attr, collect_props, BootstrapProps},
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
#[cfg(feature = "validate")]
|
||||
use validator::ValidationErrors;
|
||||
use yew::{html::Children, prelude::*};
|
||||
|
||||
#[derive(Properties, Clone, PartialEq)]
|
||||
|
@ -22,6 +24,14 @@ pub struct Props {
|
|||
#[prop_or_default]
|
||||
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
|
||||
#[prop_or_default]
|
||||
pub border: Option<Border>,
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
use super::props::Props;
|
||||
use crate::{prelude::*, render};
|
||||
#[cfg(feature = "validate")]
|
||||
use validator::ValidationErrors;
|
||||
use yew::{prelude::*, virtual_dom::VNode};
|
||||
|
||||
pub struct TextArea {
|
||||
link: ComponentLink<Self>,
|
||||
state: String,
|
||||
props: Props,
|
||||
#[cfg(feature = "validator")]
|
||||
errors: Option<ValidationErrors>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -19,23 +15,11 @@ impl Component for TextArea {
|
|||
type Message = InputChange;
|
||||
type Properties = Props;
|
||||
|
||||
#[cfg(not(feature = "validate"))]
|
||||
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||
let state = extract_state(&props);
|
||||
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 {
|
||||
if let InputChange(ChangeData::Value(value)) = msg {
|
||||
self.state = value.clone();
|
||||
|
@ -73,21 +57,16 @@ impl Component for TextArea {
|
|||
#[cfg(feature = "validate")]
|
||||
fn view(&self) -> Html {
|
||||
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 = {
|
||||
html! {
|
||||
<>
|
||||
{ feedback }
|
||||
<textarea
|
||||
readonly=self.props.readonly
|
||||
onchange=self.link.callback(|evt| InputChange(evt))
|
||||
>
|
||||
{ &self.state }
|
||||
</textarea>
|
||||
{ super::render_validation_feedback_from_props(&self.props) }
|
||||
</>
|
||||
}
|
||||
};
|
||||
|
|
10
src/props.rs
10
src/props.rs
|
@ -1,5 +1,7 @@
|
|||
use crate::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
#[cfg(feature = "validate")]
|
||||
use validator::ValidationErrors;
|
||||
use yew::{prelude::*, virtual_dom::VNode};
|
||||
|
||||
pub(crate) trait IntoBsClass {
|
||||
|
@ -32,6 +34,14 @@ pub struct Props {
|
|||
#[prop_or_default]
|
||||
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
|
||||
#[prop_or_default]
|
||||
pub border: Option<Border>,
|
||||
|
|
Loading…
Reference in a new issue