1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use yew::{Component, ComponentLink, Html, html, Properties, InputData, Callback, ShouldRender};

use crate::form::{Form};
use crate::{Model};
use crate::form_field::FormField;

pub enum FieldMessage {
    OnInput(InputData)
}

fn default_text() -> String {
    String::from("text")
}

#[derive(Properties, PartialEq, Clone)]
pub struct FieldProperties<T: Model> {
    #[prop_or_else(default_text)]
    pub input_type: String,
    pub field_name: String,
    pub form: Form<T>,
    #[prop_or_else(String::new)]
    pub placeholder: String,
    #[prop_or_else(Callback::noop)]
    pub oninput: Callback<InputData>,
}

pub struct Field<T: Model> {
    link: ComponentLink<Self>,
    pub input_type: String,
    pub field_name: String,
    pub form: Form<T>,
    pub placeholder: String,
    pub oninput: Callback<InputData>,
}

impl<T: Model> Field<T> {
    fn field(&self) -> &FormField {
        self.form.field(&self.field_name)
    }

    pub fn field_name(&self) -> &str {
        &self.field_name
    }

    pub fn class(&self) -> &str {
        let field = self.field();

        if field.dirty && field.valid {
            "form-control is-valid"
        } else if field.dirty {
            "form-control is-invalid"
        } else {
            "form-control"
        }
    }


    pub fn message(&self) -> &str {
        &self.form.field_message(&self.field_name())
    }

    pub fn valid(&self) -> bool {
        self.form.field_valid(&self.field_name())
    }

    pub fn dirty(&self) -> bool {
        self.field().dirty
    }

    pub fn set_field(&mut self, field_name: &str, value: &str) {
        self.form.set_field_value(field_name, value)
    }
}

impl<T: Model> Component for Field<T> {
    type Message = FieldMessage;
    type Properties = FieldProperties<T>;

    fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
        let mut form_field = Self {
            link,
            input_type: String::from(props.input_type),
            field_name: String::from(props.field_name),
            form: props.form,
            placeholder: String::from(props.placeholder),
            oninput: props.oninput,
        };

        if form_field.input_type == "" {
            form_field.input_type = String::from("text");
        }

        form_field
    }

    fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {
            FieldMessage::OnInput(input_data) => {
                let state = self.form.state_mut();
                state.set_field_value(&self.field_name, &input_data.value);
                state.update_validation();

                self.oninput.emit(input_data);
                true
            }
        }
    }

    fn change(&mut self, _props: Self::Properties) -> ShouldRender {
        true
    }

    fn view(&self) -> Html {
        html! {
            <input
                class=self.class()
                id=self.field_name
                type=self.input_type
                placeholder=self.placeholder
                value=self.form.field_value(&self.field_name)
                oninput=self.link.callback(|e: InputData| FieldMessage::OnInput(e))
            />
        }
    }
}