nova_forms/
wiring.rs

1use std::{fmt::Display, str::FromStr};
2
3use crate::{use_translation, FormContext, InputContext, QueryString, QueryStringPart};
4use leptos::*;
5
6/// Used to wire an input field to the form context.
7/// For example, refer to the input field components in this library.
8pub struct FieldWiring<T>
9where
10    T: FromStr + ToString + Clone + Default + 'static,
11    T::Err: Clone + Display,
12{
13    pub qs: QueryString,
14    pub value: Signal<Result<T, T::Err>>,
15    pub raw_value: Signal<String>,
16    pub set_value: Callback<T, ()>,
17    pub set_raw_value: Callback<String, ()>,
18    pub error: Signal<Option<Oco<'static, str>>>,
19    pub render_mode: Signal<bool>,
20    pub disabled: Signal<bool>,
21}
22
23impl<T> FieldWiring<T>
24where
25    T: FromStr + ToString + Clone + Default + 'static,
26    T::Err: Clone + Display
27{
28    pub fn wire(
29        bind: QueryStringPart,
30        external_value: MaybeProp<T>,
31        change: Option<Callback<Result<T, T::Err>, ()>>,
32        error: MaybeProp<TextProp>,
33        label: TextProp,
34    ) -> Self {
35        let input = InputContext::new(bind);  
36        input.add_label(label);
37        let qs = input.qs();
38        let validate_signal = input.validate_signal();
39        let nova_form_context = expect_context::<FormContext>();
40        let form_value = input.value();
41        let raw_form_value = input.raw_value();
42        let (show_error, set_show_error) = create_signal(false);
43
44        // Set debug value.
45        if cfg!(debug_assertions) {
46            input.set_value(T::default());
47        }
48
49        // Call change callback if value changed.
50        if let Some(change) = change {
51            create_effect(move |_| {
52                logging::log!("call change callback");
53                change.call(form_value.get());
54            });
55        }
56    
57        // Update value
58        create_effect(move |_| {
59            if let Some(value) = external_value.get() {
60                logging::log!("update value from external");
61                input.set_value(value);
62            }
63        });
64    
65        let set_raw_value = Callback::new(move |value: String| {
66            logging::log!("set_raw_value {}: {}", qs, value);
67            input.set_raw_value(value);
68            set_show_error.set(true);
69        });
70
71        let set_value = Callback::new(move |value: T| {
72            logging::log!("set_value {}: {}", qs, value.to_string());
73            input.set_value(value);
74            set_show_error.set(true);
75        });
76
77        create_effect(move |_| {
78            logging::log!("validate_signal {}", qs);
79            if validate_signal.get() {
80                set_show_error.set(true);
81            }
82        });
83
84        create_effect(move |_| {
85            match form_value.get() {
86                Ok(value) => {
87                    logging::log!("form_value changed {}: {}", qs, value.to_string());
88                }
89                Err(err) => {
90                    logging::log!("form_value changed {}: {}", qs, err);
91                }
92            }
93        });
94
95        let error_message = Memo::new(move |_| {
96            logging::log!("error_message {}", qs);
97            if let Some(error) = error.get() {
98                Some(error.get())
99            } else
100            if show_error.get() {
101                form_value.get().err().map(|err| use_translation(err).get())
102            } else {
103                None
104            }
105        }).into();
106
107        let disabled = input.disabled();
108
109        let render_mode = Signal::derive(move || nova_form_context.is_render_mode());
110    
111        FieldWiring {
112            qs,
113            value: form_value,
114            raw_value: raw_form_value,
115            set_raw_value,
116            set_value,
117            error: error_message,
118            render_mode,
119            disabled,
120        }
121    }
122}