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)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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! {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
|
@ -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) }
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
10
src/props.rs
10
src/props.rs
|
@ -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>,
|
||||||
|
|
Loading…
Reference in a new issue