Skip to main content

FormFieldBuilder

Struct FormFieldBuilder 

Source
pub struct FormFieldBuilder { /* private fields */ }
Expand description

Builder for FormField views.

Implementations§

Source§

impl FormFieldBuilder

Source

pub fn new(name: impl Into<String>) -> Self

Source

pub fn label(self, label: impl Into<String>) -> Self

Set the display label.

Examples found in repository?
examples/22_forms.rs (line 148)
21    fn render(&self, cx: Scope) -> View {
22        let show_help = state!(cx, || false);
23
24        // F1 toggles help
25        cx.use_command(
26            KeyBinding::key(KeyCode::F(1)),
27            with!(show_help => move || show_help.update(|v| *v = !*v)),
28        );
29
30        // Create form state with validated fields
31        let form = state!(cx, || {
32            FormState::new()
33                .field(
34                    FieldBuilder::new("email")
35                        .required()
36                        .email()
37                        .error_message("Please enter a valid email address")
38                        .build(),
39                )
40                .field(
41                    FieldBuilder::new("password")
42                        .required()
43                        .min_length(8)
44                        .error_message("Password must be at least 8 characters")
45                        .build(),
46                )
47                .field(
48                    FieldBuilder::new("username")
49                        .required()
50                        .min_length(3)
51                        .max_length(20)
52                        .custom(|v| {
53                            if v.contains(' ') {
54                                Some("Username cannot contain spaces".to_string())
55                            } else if !v.chars().all(|c| c.is_alphanumeric() || c == '_') {
56                                Some(
57                                    "Username can only contain letters, numbers, and underscores"
58                                        .to_string(),
59                                )
60                            } else {
61                                None
62                            }
63                        })
64                        .build(),
65                )
66                .field(
67                    FieldBuilder::new("age")
68                        .integer()
69                        .custom(|v| {
70                            if v.is_empty() {
71                                return None; // Optional field
72                            }
73                            match v.parse::<i32>() {
74                                Ok(age) if age < 0 => Some("Age cannot be negative".to_string()),
75                                Ok(age) if age > 150 => {
76                                    Some("Please enter a valid age".to_string())
77                                }
78                                Ok(_) => None,
79                                Err(_) => Some("Please enter a valid number".to_string()),
80                            }
81                        })
82                        .build(),
83                )
84        });
85
86        let submit_message = state!(cx, String::new);
87
88        // Get current field values and errors
89        let email = form.get().get_value("email");
90        let password = form.get().get_value("password");
91        let username = form.get().get_value("username");
92        let age = form.get().get_value("age");
93
94        let email_error = form.get().get_error("email");
95        let password_error = form.get().get_error("password");
96        let username_error = form.get().get_error("username");
97        let age_error = form.get().get_error("age");
98
99        // Handlers
100        let on_submit = with!(form, submit_message => move || {
101            if form.get().validate() {
102                let values = form.get().values();
103                submit_message.set(format!(
104                    "Form submitted! Email: {}, Username: {}",
105                    values.get("email").unwrap_or(&String::new()),
106                    values.get("username").unwrap_or(&String::new())
107                ));
108            } else {
109                submit_message.set("Please fix the errors above".to_string());
110            }
111        });
112
113        let on_reset = with!(form, submit_message => move || {
114            form.get().reset();
115            submit_message.set(String::new());
116        });
117
118        View::vstack()
119            .spacing(1)
120            .child(
121                // Header
122                View::boxed()
123                    .border(true)
124                    .padding(1)
125                    .child(
126                        View::vstack()
127                            .child(View::styled_text("Form Validation Demo").bold().build())
128                            .child(
129                                View::styled_text("Tab between fields, type to enter values")
130                                    .dim()
131                                    .build(),
132                            )
133                            .build(),
134                    )
135                    .build(),
136            )
137            .child(
138                // Form
139                View::boxed()
140                    .flex(1)
141                    .border(true)
142                    .padding(1)
143                    .child(
144                        View::form()
145                            .spacing(1)
146                            .child(
147                                View::form_field("email")
148                                    .label("Email Address *")
149                                    .value(email.clone())
150                                    .placeholder("you@example.com")
151                                    .error(email_error)
152                                    .on_change(with!(form => move |v: String| {
153                                        form.get().set_value("email", v);
154                                    }))
155                                    .on_blur(with!(form => move || {
156                                        form.get().touch("email");
157                                    }))
158                                    .build(),
159                            )
160                            .child(
161                                View::form_field("username")
162                                    .label("Username *")
163                                    .value(username.clone())
164                                    .placeholder("johndoe")
165                                    .error(username_error)
166                                    .on_change(with!(form => move |v: String| {
167                                        form.get().set_value("username", v);
168                                    }))
169                                    .on_blur(with!(form => move || {
170                                        form.get().touch("username");
171                                    }))
172                                    .build(),
173                            )
174                            .child(
175                                View::form_field("password")
176                                    .label("Password * (min 8 chars)")
177                                    .value(password.clone())
178                                    .placeholder("Enter password")
179                                    .password(true)
180                                    .error(password_error)
181                                    .on_change(with!(form => move |v: String| {
182                                        form.get().set_value("password", v);
183                                    }))
184                                    .on_blur(with!(form => move || {
185                                        form.get().touch("password");
186                                    }))
187                                    .build(),
188                            )
189                            .child(
190                                View::form_field("age")
191                                    .label("Age (optional)")
192                                    .value(age.clone())
193                                    .placeholder("25")
194                                    .error(age_error)
195                                    .on_change(with!(form => move |v: String| {
196                                        form.get().set_value("age", v);
197                                    }))
198                                    .on_blur(with!(form => move || {
199                                        form.get().touch("age");
200                                    }))
201                                    .build(),
202                            )
203                            .build(),
204                    )
205                    .build(),
206            )
207            .child(
208                // Actions
209                View::hstack()
210                    .spacing(2)
211                    .child(View::button().label("Submit").on_press(on_submit).build())
212                    .child(View::button().label("Reset").on_press(on_reset).build())
213                    .build(),
214            )
215            .child(
216                // Status
217                View::boxed()
218                    .border(true)
219                    .padding(1)
220                    .child(
221                        View::vstack()
222                            .child(View::text(if submit_message.get().is_empty() {
223                                "Fill in the form and click Submit".to_string()
224                            } else {
225                                submit_message.get()
226                            }))
227                            .child(
228                                View::styled_text(format!(
229                                    "Form valid: {}",
230                                    if form.get().is_valid() { "Yes" } else { "No" }
231                                ))
232                                .color(if form.get().is_valid() {
233                                    Color::Green
234                                } else {
235                                    Color::Red
236                                })
237                                .build(),
238                            )
239                            .build(),
240                    )
241                    .build(),
242            )
243            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
244            .child(
245                View::modal()
246                    .visible(show_help.get())
247                    .title("Example 22: Forms")
248                    .on_dismiss(with!(show_help => move || show_help.set(false)))
249                    .child(
250                        View::vstack()
251                            .child(View::styled_text("What you're seeing").bold().build())
252                            .child(View::text("• Declarative form validation"))
253                            .child(View::text("• Required, email, length validators"))
254                            .child(View::text("• Custom validation functions"))
255                            .child(View::gap(1))
256                            .child(View::styled_text("Key concepts").bold().build())
257                            .child(View::text("• FormState manages all fields"))
258                            .child(View::text("• FieldBuilder defines validation"))
259                            .child(View::text("• View::form_field() renders inputs"))
260                            .child(View::text("• on_blur triggers validation"))
261                            .child(View::gap(1))
262                            .child(View::styled_text("Try this").bold().build())
263                            .child(View::text("• Enter invalid email, see error"))
264                            .child(View::text("• Try short password (<8 chars)"))
265                            .child(View::text("• Username with spaces shows error"))
266                            .child(View::gap(1))
267                            .child(View::styled_text("Next up").bold().build())
268                            .child(View::text("→ 23_modal: modal dialogs"))
269                            .child(View::gap(1))
270                            .child(View::styled_text("Press Escape to close").dim().build())
271                            .build(),
272                    )
273                    .build(),
274            )
275            .build()
276    }
Source

pub fn value(self, value: impl Into<String>) -> Self

Set the current value.

Examples found in repository?
examples/22_forms.rs (line 149)
21    fn render(&self, cx: Scope) -> View {
22        let show_help = state!(cx, || false);
23
24        // F1 toggles help
25        cx.use_command(
26            KeyBinding::key(KeyCode::F(1)),
27            with!(show_help => move || show_help.update(|v| *v = !*v)),
28        );
29
30        // Create form state with validated fields
31        let form = state!(cx, || {
32            FormState::new()
33                .field(
34                    FieldBuilder::new("email")
35                        .required()
36                        .email()
37                        .error_message("Please enter a valid email address")
38                        .build(),
39                )
40                .field(
41                    FieldBuilder::new("password")
42                        .required()
43                        .min_length(8)
44                        .error_message("Password must be at least 8 characters")
45                        .build(),
46                )
47                .field(
48                    FieldBuilder::new("username")
49                        .required()
50                        .min_length(3)
51                        .max_length(20)
52                        .custom(|v| {
53                            if v.contains(' ') {
54                                Some("Username cannot contain spaces".to_string())
55                            } else if !v.chars().all(|c| c.is_alphanumeric() || c == '_') {
56                                Some(
57                                    "Username can only contain letters, numbers, and underscores"
58                                        .to_string(),
59                                )
60                            } else {
61                                None
62                            }
63                        })
64                        .build(),
65                )
66                .field(
67                    FieldBuilder::new("age")
68                        .integer()
69                        .custom(|v| {
70                            if v.is_empty() {
71                                return None; // Optional field
72                            }
73                            match v.parse::<i32>() {
74                                Ok(age) if age < 0 => Some("Age cannot be negative".to_string()),
75                                Ok(age) if age > 150 => {
76                                    Some("Please enter a valid age".to_string())
77                                }
78                                Ok(_) => None,
79                                Err(_) => Some("Please enter a valid number".to_string()),
80                            }
81                        })
82                        .build(),
83                )
84        });
85
86        let submit_message = state!(cx, String::new);
87
88        // Get current field values and errors
89        let email = form.get().get_value("email");
90        let password = form.get().get_value("password");
91        let username = form.get().get_value("username");
92        let age = form.get().get_value("age");
93
94        let email_error = form.get().get_error("email");
95        let password_error = form.get().get_error("password");
96        let username_error = form.get().get_error("username");
97        let age_error = form.get().get_error("age");
98
99        // Handlers
100        let on_submit = with!(form, submit_message => move || {
101            if form.get().validate() {
102                let values = form.get().values();
103                submit_message.set(format!(
104                    "Form submitted! Email: {}, Username: {}",
105                    values.get("email").unwrap_or(&String::new()),
106                    values.get("username").unwrap_or(&String::new())
107                ));
108            } else {
109                submit_message.set("Please fix the errors above".to_string());
110            }
111        });
112
113        let on_reset = with!(form, submit_message => move || {
114            form.get().reset();
115            submit_message.set(String::new());
116        });
117
118        View::vstack()
119            .spacing(1)
120            .child(
121                // Header
122                View::boxed()
123                    .border(true)
124                    .padding(1)
125                    .child(
126                        View::vstack()
127                            .child(View::styled_text("Form Validation Demo").bold().build())
128                            .child(
129                                View::styled_text("Tab between fields, type to enter values")
130                                    .dim()
131                                    .build(),
132                            )
133                            .build(),
134                    )
135                    .build(),
136            )
137            .child(
138                // Form
139                View::boxed()
140                    .flex(1)
141                    .border(true)
142                    .padding(1)
143                    .child(
144                        View::form()
145                            .spacing(1)
146                            .child(
147                                View::form_field("email")
148                                    .label("Email Address *")
149                                    .value(email.clone())
150                                    .placeholder("you@example.com")
151                                    .error(email_error)
152                                    .on_change(with!(form => move |v: String| {
153                                        form.get().set_value("email", v);
154                                    }))
155                                    .on_blur(with!(form => move || {
156                                        form.get().touch("email");
157                                    }))
158                                    .build(),
159                            )
160                            .child(
161                                View::form_field("username")
162                                    .label("Username *")
163                                    .value(username.clone())
164                                    .placeholder("johndoe")
165                                    .error(username_error)
166                                    .on_change(with!(form => move |v: String| {
167                                        form.get().set_value("username", v);
168                                    }))
169                                    .on_blur(with!(form => move || {
170                                        form.get().touch("username");
171                                    }))
172                                    .build(),
173                            )
174                            .child(
175                                View::form_field("password")
176                                    .label("Password * (min 8 chars)")
177                                    .value(password.clone())
178                                    .placeholder("Enter password")
179                                    .password(true)
180                                    .error(password_error)
181                                    .on_change(with!(form => move |v: String| {
182                                        form.get().set_value("password", v);
183                                    }))
184                                    .on_blur(with!(form => move || {
185                                        form.get().touch("password");
186                                    }))
187                                    .build(),
188                            )
189                            .child(
190                                View::form_field("age")
191                                    .label("Age (optional)")
192                                    .value(age.clone())
193                                    .placeholder("25")
194                                    .error(age_error)
195                                    .on_change(with!(form => move |v: String| {
196                                        form.get().set_value("age", v);
197                                    }))
198                                    .on_blur(with!(form => move || {
199                                        form.get().touch("age");
200                                    }))
201                                    .build(),
202                            )
203                            .build(),
204                    )
205                    .build(),
206            )
207            .child(
208                // Actions
209                View::hstack()
210                    .spacing(2)
211                    .child(View::button().label("Submit").on_press(on_submit).build())
212                    .child(View::button().label("Reset").on_press(on_reset).build())
213                    .build(),
214            )
215            .child(
216                // Status
217                View::boxed()
218                    .border(true)
219                    .padding(1)
220                    .child(
221                        View::vstack()
222                            .child(View::text(if submit_message.get().is_empty() {
223                                "Fill in the form and click Submit".to_string()
224                            } else {
225                                submit_message.get()
226                            }))
227                            .child(
228                                View::styled_text(format!(
229                                    "Form valid: {}",
230                                    if form.get().is_valid() { "Yes" } else { "No" }
231                                ))
232                                .color(if form.get().is_valid() {
233                                    Color::Green
234                                } else {
235                                    Color::Red
236                                })
237                                .build(),
238                            )
239                            .build(),
240                    )
241                    .build(),
242            )
243            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
244            .child(
245                View::modal()
246                    .visible(show_help.get())
247                    .title("Example 22: Forms")
248                    .on_dismiss(with!(show_help => move || show_help.set(false)))
249                    .child(
250                        View::vstack()
251                            .child(View::styled_text("What you're seeing").bold().build())
252                            .child(View::text("• Declarative form validation"))
253                            .child(View::text("• Required, email, length validators"))
254                            .child(View::text("• Custom validation functions"))
255                            .child(View::gap(1))
256                            .child(View::styled_text("Key concepts").bold().build())
257                            .child(View::text("• FormState manages all fields"))
258                            .child(View::text("• FieldBuilder defines validation"))
259                            .child(View::text("• View::form_field() renders inputs"))
260                            .child(View::text("• on_blur triggers validation"))
261                            .child(View::gap(1))
262                            .child(View::styled_text("Try this").bold().build())
263                            .child(View::text("• Enter invalid email, see error"))
264                            .child(View::text("• Try short password (<8 chars)"))
265                            .child(View::text("• Username with spaces shows error"))
266                            .child(View::gap(1))
267                            .child(View::styled_text("Next up").bold().build())
268                            .child(View::text("→ 23_modal: modal dialogs"))
269                            .child(View::gap(1))
270                            .child(View::styled_text("Press Escape to close").dim().build())
271                            .build(),
272                    )
273                    .build(),
274            )
275            .build()
276    }
Source

pub fn placeholder(self, placeholder: impl Into<String>) -> Self

Set the placeholder text.

Examples found in repository?
examples/22_forms.rs (line 150)
21    fn render(&self, cx: Scope) -> View {
22        let show_help = state!(cx, || false);
23
24        // F1 toggles help
25        cx.use_command(
26            KeyBinding::key(KeyCode::F(1)),
27            with!(show_help => move || show_help.update(|v| *v = !*v)),
28        );
29
30        // Create form state with validated fields
31        let form = state!(cx, || {
32            FormState::new()
33                .field(
34                    FieldBuilder::new("email")
35                        .required()
36                        .email()
37                        .error_message("Please enter a valid email address")
38                        .build(),
39                )
40                .field(
41                    FieldBuilder::new("password")
42                        .required()
43                        .min_length(8)
44                        .error_message("Password must be at least 8 characters")
45                        .build(),
46                )
47                .field(
48                    FieldBuilder::new("username")
49                        .required()
50                        .min_length(3)
51                        .max_length(20)
52                        .custom(|v| {
53                            if v.contains(' ') {
54                                Some("Username cannot contain spaces".to_string())
55                            } else if !v.chars().all(|c| c.is_alphanumeric() || c == '_') {
56                                Some(
57                                    "Username can only contain letters, numbers, and underscores"
58                                        .to_string(),
59                                )
60                            } else {
61                                None
62                            }
63                        })
64                        .build(),
65                )
66                .field(
67                    FieldBuilder::new("age")
68                        .integer()
69                        .custom(|v| {
70                            if v.is_empty() {
71                                return None; // Optional field
72                            }
73                            match v.parse::<i32>() {
74                                Ok(age) if age < 0 => Some("Age cannot be negative".to_string()),
75                                Ok(age) if age > 150 => {
76                                    Some("Please enter a valid age".to_string())
77                                }
78                                Ok(_) => None,
79                                Err(_) => Some("Please enter a valid number".to_string()),
80                            }
81                        })
82                        .build(),
83                )
84        });
85
86        let submit_message = state!(cx, String::new);
87
88        // Get current field values and errors
89        let email = form.get().get_value("email");
90        let password = form.get().get_value("password");
91        let username = form.get().get_value("username");
92        let age = form.get().get_value("age");
93
94        let email_error = form.get().get_error("email");
95        let password_error = form.get().get_error("password");
96        let username_error = form.get().get_error("username");
97        let age_error = form.get().get_error("age");
98
99        // Handlers
100        let on_submit = with!(form, submit_message => move || {
101            if form.get().validate() {
102                let values = form.get().values();
103                submit_message.set(format!(
104                    "Form submitted! Email: {}, Username: {}",
105                    values.get("email").unwrap_or(&String::new()),
106                    values.get("username").unwrap_or(&String::new())
107                ));
108            } else {
109                submit_message.set("Please fix the errors above".to_string());
110            }
111        });
112
113        let on_reset = with!(form, submit_message => move || {
114            form.get().reset();
115            submit_message.set(String::new());
116        });
117
118        View::vstack()
119            .spacing(1)
120            .child(
121                // Header
122                View::boxed()
123                    .border(true)
124                    .padding(1)
125                    .child(
126                        View::vstack()
127                            .child(View::styled_text("Form Validation Demo").bold().build())
128                            .child(
129                                View::styled_text("Tab between fields, type to enter values")
130                                    .dim()
131                                    .build(),
132                            )
133                            .build(),
134                    )
135                    .build(),
136            )
137            .child(
138                // Form
139                View::boxed()
140                    .flex(1)
141                    .border(true)
142                    .padding(1)
143                    .child(
144                        View::form()
145                            .spacing(1)
146                            .child(
147                                View::form_field("email")
148                                    .label("Email Address *")
149                                    .value(email.clone())
150                                    .placeholder("you@example.com")
151                                    .error(email_error)
152                                    .on_change(with!(form => move |v: String| {
153                                        form.get().set_value("email", v);
154                                    }))
155                                    .on_blur(with!(form => move || {
156                                        form.get().touch("email");
157                                    }))
158                                    .build(),
159                            )
160                            .child(
161                                View::form_field("username")
162                                    .label("Username *")
163                                    .value(username.clone())
164                                    .placeholder("johndoe")
165                                    .error(username_error)
166                                    .on_change(with!(form => move |v: String| {
167                                        form.get().set_value("username", v);
168                                    }))
169                                    .on_blur(with!(form => move || {
170                                        form.get().touch("username");
171                                    }))
172                                    .build(),
173                            )
174                            .child(
175                                View::form_field("password")
176                                    .label("Password * (min 8 chars)")
177                                    .value(password.clone())
178                                    .placeholder("Enter password")
179                                    .password(true)
180                                    .error(password_error)
181                                    .on_change(with!(form => move |v: String| {
182                                        form.get().set_value("password", v);
183                                    }))
184                                    .on_blur(with!(form => move || {
185                                        form.get().touch("password");
186                                    }))
187                                    .build(),
188                            )
189                            .child(
190                                View::form_field("age")
191                                    .label("Age (optional)")
192                                    .value(age.clone())
193                                    .placeholder("25")
194                                    .error(age_error)
195                                    .on_change(with!(form => move |v: String| {
196                                        form.get().set_value("age", v);
197                                    }))
198                                    .on_blur(with!(form => move || {
199                                        form.get().touch("age");
200                                    }))
201                                    .build(),
202                            )
203                            .build(),
204                    )
205                    .build(),
206            )
207            .child(
208                // Actions
209                View::hstack()
210                    .spacing(2)
211                    .child(View::button().label("Submit").on_press(on_submit).build())
212                    .child(View::button().label("Reset").on_press(on_reset).build())
213                    .build(),
214            )
215            .child(
216                // Status
217                View::boxed()
218                    .border(true)
219                    .padding(1)
220                    .child(
221                        View::vstack()
222                            .child(View::text(if submit_message.get().is_empty() {
223                                "Fill in the form and click Submit".to_string()
224                            } else {
225                                submit_message.get()
226                            }))
227                            .child(
228                                View::styled_text(format!(
229                                    "Form valid: {}",
230                                    if form.get().is_valid() { "Yes" } else { "No" }
231                                ))
232                                .color(if form.get().is_valid() {
233                                    Color::Green
234                                } else {
235                                    Color::Red
236                                })
237                                .build(),
238                            )
239                            .build(),
240                    )
241                    .build(),
242            )
243            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
244            .child(
245                View::modal()
246                    .visible(show_help.get())
247                    .title("Example 22: Forms")
248                    .on_dismiss(with!(show_help => move || show_help.set(false)))
249                    .child(
250                        View::vstack()
251                            .child(View::styled_text("What you're seeing").bold().build())
252                            .child(View::text("• Declarative form validation"))
253                            .child(View::text("• Required, email, length validators"))
254                            .child(View::text("• Custom validation functions"))
255                            .child(View::gap(1))
256                            .child(View::styled_text("Key concepts").bold().build())
257                            .child(View::text("• FormState manages all fields"))
258                            .child(View::text("• FieldBuilder defines validation"))
259                            .child(View::text("• View::form_field() renders inputs"))
260                            .child(View::text("• on_blur triggers validation"))
261                            .child(View::gap(1))
262                            .child(View::styled_text("Try this").bold().build())
263                            .child(View::text("• Enter invalid email, see error"))
264                            .child(View::text("• Try short password (<8 chars)"))
265                            .child(View::text("• Username with spaces shows error"))
266                            .child(View::gap(1))
267                            .child(View::styled_text("Next up").bold().build())
268                            .child(View::text("→ 23_modal: modal dialogs"))
269                            .child(View::gap(1))
270                            .child(View::styled_text("Press Escape to close").dim().build())
271                            .build(),
272                    )
273                    .build(),
274            )
275            .build()
276    }
Source

pub fn error(self, error: Option<String>) -> Self

Set the error message.

Examples found in repository?
examples/22_forms.rs (line 151)
21    fn render(&self, cx: Scope) -> View {
22        let show_help = state!(cx, || false);
23
24        // F1 toggles help
25        cx.use_command(
26            KeyBinding::key(KeyCode::F(1)),
27            with!(show_help => move || show_help.update(|v| *v = !*v)),
28        );
29
30        // Create form state with validated fields
31        let form = state!(cx, || {
32            FormState::new()
33                .field(
34                    FieldBuilder::new("email")
35                        .required()
36                        .email()
37                        .error_message("Please enter a valid email address")
38                        .build(),
39                )
40                .field(
41                    FieldBuilder::new("password")
42                        .required()
43                        .min_length(8)
44                        .error_message("Password must be at least 8 characters")
45                        .build(),
46                )
47                .field(
48                    FieldBuilder::new("username")
49                        .required()
50                        .min_length(3)
51                        .max_length(20)
52                        .custom(|v| {
53                            if v.contains(' ') {
54                                Some("Username cannot contain spaces".to_string())
55                            } else if !v.chars().all(|c| c.is_alphanumeric() || c == '_') {
56                                Some(
57                                    "Username can only contain letters, numbers, and underscores"
58                                        .to_string(),
59                                )
60                            } else {
61                                None
62                            }
63                        })
64                        .build(),
65                )
66                .field(
67                    FieldBuilder::new("age")
68                        .integer()
69                        .custom(|v| {
70                            if v.is_empty() {
71                                return None; // Optional field
72                            }
73                            match v.parse::<i32>() {
74                                Ok(age) if age < 0 => Some("Age cannot be negative".to_string()),
75                                Ok(age) if age > 150 => {
76                                    Some("Please enter a valid age".to_string())
77                                }
78                                Ok(_) => None,
79                                Err(_) => Some("Please enter a valid number".to_string()),
80                            }
81                        })
82                        .build(),
83                )
84        });
85
86        let submit_message = state!(cx, String::new);
87
88        // Get current field values and errors
89        let email = form.get().get_value("email");
90        let password = form.get().get_value("password");
91        let username = form.get().get_value("username");
92        let age = form.get().get_value("age");
93
94        let email_error = form.get().get_error("email");
95        let password_error = form.get().get_error("password");
96        let username_error = form.get().get_error("username");
97        let age_error = form.get().get_error("age");
98
99        // Handlers
100        let on_submit = with!(form, submit_message => move || {
101            if form.get().validate() {
102                let values = form.get().values();
103                submit_message.set(format!(
104                    "Form submitted! Email: {}, Username: {}",
105                    values.get("email").unwrap_or(&String::new()),
106                    values.get("username").unwrap_or(&String::new())
107                ));
108            } else {
109                submit_message.set("Please fix the errors above".to_string());
110            }
111        });
112
113        let on_reset = with!(form, submit_message => move || {
114            form.get().reset();
115            submit_message.set(String::new());
116        });
117
118        View::vstack()
119            .spacing(1)
120            .child(
121                // Header
122                View::boxed()
123                    .border(true)
124                    .padding(1)
125                    .child(
126                        View::vstack()
127                            .child(View::styled_text("Form Validation Demo").bold().build())
128                            .child(
129                                View::styled_text("Tab between fields, type to enter values")
130                                    .dim()
131                                    .build(),
132                            )
133                            .build(),
134                    )
135                    .build(),
136            )
137            .child(
138                // Form
139                View::boxed()
140                    .flex(1)
141                    .border(true)
142                    .padding(1)
143                    .child(
144                        View::form()
145                            .spacing(1)
146                            .child(
147                                View::form_field("email")
148                                    .label("Email Address *")
149                                    .value(email.clone())
150                                    .placeholder("you@example.com")
151                                    .error(email_error)
152                                    .on_change(with!(form => move |v: String| {
153                                        form.get().set_value("email", v);
154                                    }))
155                                    .on_blur(with!(form => move || {
156                                        form.get().touch("email");
157                                    }))
158                                    .build(),
159                            )
160                            .child(
161                                View::form_field("username")
162                                    .label("Username *")
163                                    .value(username.clone())
164                                    .placeholder("johndoe")
165                                    .error(username_error)
166                                    .on_change(with!(form => move |v: String| {
167                                        form.get().set_value("username", v);
168                                    }))
169                                    .on_blur(with!(form => move || {
170                                        form.get().touch("username");
171                                    }))
172                                    .build(),
173                            )
174                            .child(
175                                View::form_field("password")
176                                    .label("Password * (min 8 chars)")
177                                    .value(password.clone())
178                                    .placeholder("Enter password")
179                                    .password(true)
180                                    .error(password_error)
181                                    .on_change(with!(form => move |v: String| {
182                                        form.get().set_value("password", v);
183                                    }))
184                                    .on_blur(with!(form => move || {
185                                        form.get().touch("password");
186                                    }))
187                                    .build(),
188                            )
189                            .child(
190                                View::form_field("age")
191                                    .label("Age (optional)")
192                                    .value(age.clone())
193                                    .placeholder("25")
194                                    .error(age_error)
195                                    .on_change(with!(form => move |v: String| {
196                                        form.get().set_value("age", v);
197                                    }))
198                                    .on_blur(with!(form => move || {
199                                        form.get().touch("age");
200                                    }))
201                                    .build(),
202                            )
203                            .build(),
204                    )
205                    .build(),
206            )
207            .child(
208                // Actions
209                View::hstack()
210                    .spacing(2)
211                    .child(View::button().label("Submit").on_press(on_submit).build())
212                    .child(View::button().label("Reset").on_press(on_reset).build())
213                    .build(),
214            )
215            .child(
216                // Status
217                View::boxed()
218                    .border(true)
219                    .padding(1)
220                    .child(
221                        View::vstack()
222                            .child(View::text(if submit_message.get().is_empty() {
223                                "Fill in the form and click Submit".to_string()
224                            } else {
225                                submit_message.get()
226                            }))
227                            .child(
228                                View::styled_text(format!(
229                                    "Form valid: {}",
230                                    if form.get().is_valid() { "Yes" } else { "No" }
231                                ))
232                                .color(if form.get().is_valid() {
233                                    Color::Green
234                                } else {
235                                    Color::Red
236                                })
237                                .build(),
238                            )
239                            .build(),
240                    )
241                    .build(),
242            )
243            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
244            .child(
245                View::modal()
246                    .visible(show_help.get())
247                    .title("Example 22: Forms")
248                    .on_dismiss(with!(show_help => move || show_help.set(false)))
249                    .child(
250                        View::vstack()
251                            .child(View::styled_text("What you're seeing").bold().build())
252                            .child(View::text("• Declarative form validation"))
253                            .child(View::text("• Required, email, length validators"))
254                            .child(View::text("• Custom validation functions"))
255                            .child(View::gap(1))
256                            .child(View::styled_text("Key concepts").bold().build())
257                            .child(View::text("• FormState manages all fields"))
258                            .child(View::text("• FieldBuilder defines validation"))
259                            .child(View::text("• View::form_field() renders inputs"))
260                            .child(View::text("• on_blur triggers validation"))
261                            .child(View::gap(1))
262                            .child(View::styled_text("Try this").bold().build())
263                            .child(View::text("• Enter invalid email, see error"))
264                            .child(View::text("• Try short password (<8 chars)"))
265                            .child(View::text("• Username with spaces shows error"))
266                            .child(View::gap(1))
267                            .child(View::styled_text("Next up").bold().build())
268                            .child(View::text("→ 23_modal: modal dialogs"))
269                            .child(View::gap(1))
270                            .child(View::styled_text("Press Escape to close").dim().build())
271                            .build(),
272                    )
273                    .build(),
274            )
275            .build()
276    }
Source

pub fn password(self, password: bool) -> Self

Set whether this is a password field.

Examples found in repository?
examples/22_forms.rs (line 179)
21    fn render(&self, cx: Scope) -> View {
22        let show_help = state!(cx, || false);
23
24        // F1 toggles help
25        cx.use_command(
26            KeyBinding::key(KeyCode::F(1)),
27            with!(show_help => move || show_help.update(|v| *v = !*v)),
28        );
29
30        // Create form state with validated fields
31        let form = state!(cx, || {
32            FormState::new()
33                .field(
34                    FieldBuilder::new("email")
35                        .required()
36                        .email()
37                        .error_message("Please enter a valid email address")
38                        .build(),
39                )
40                .field(
41                    FieldBuilder::new("password")
42                        .required()
43                        .min_length(8)
44                        .error_message("Password must be at least 8 characters")
45                        .build(),
46                )
47                .field(
48                    FieldBuilder::new("username")
49                        .required()
50                        .min_length(3)
51                        .max_length(20)
52                        .custom(|v| {
53                            if v.contains(' ') {
54                                Some("Username cannot contain spaces".to_string())
55                            } else if !v.chars().all(|c| c.is_alphanumeric() || c == '_') {
56                                Some(
57                                    "Username can only contain letters, numbers, and underscores"
58                                        .to_string(),
59                                )
60                            } else {
61                                None
62                            }
63                        })
64                        .build(),
65                )
66                .field(
67                    FieldBuilder::new("age")
68                        .integer()
69                        .custom(|v| {
70                            if v.is_empty() {
71                                return None; // Optional field
72                            }
73                            match v.parse::<i32>() {
74                                Ok(age) if age < 0 => Some("Age cannot be negative".to_string()),
75                                Ok(age) if age > 150 => {
76                                    Some("Please enter a valid age".to_string())
77                                }
78                                Ok(_) => None,
79                                Err(_) => Some("Please enter a valid number".to_string()),
80                            }
81                        })
82                        .build(),
83                )
84        });
85
86        let submit_message = state!(cx, String::new);
87
88        // Get current field values and errors
89        let email = form.get().get_value("email");
90        let password = form.get().get_value("password");
91        let username = form.get().get_value("username");
92        let age = form.get().get_value("age");
93
94        let email_error = form.get().get_error("email");
95        let password_error = form.get().get_error("password");
96        let username_error = form.get().get_error("username");
97        let age_error = form.get().get_error("age");
98
99        // Handlers
100        let on_submit = with!(form, submit_message => move || {
101            if form.get().validate() {
102                let values = form.get().values();
103                submit_message.set(format!(
104                    "Form submitted! Email: {}, Username: {}",
105                    values.get("email").unwrap_or(&String::new()),
106                    values.get("username").unwrap_or(&String::new())
107                ));
108            } else {
109                submit_message.set("Please fix the errors above".to_string());
110            }
111        });
112
113        let on_reset = with!(form, submit_message => move || {
114            form.get().reset();
115            submit_message.set(String::new());
116        });
117
118        View::vstack()
119            .spacing(1)
120            .child(
121                // Header
122                View::boxed()
123                    .border(true)
124                    .padding(1)
125                    .child(
126                        View::vstack()
127                            .child(View::styled_text("Form Validation Demo").bold().build())
128                            .child(
129                                View::styled_text("Tab between fields, type to enter values")
130                                    .dim()
131                                    .build(),
132                            )
133                            .build(),
134                    )
135                    .build(),
136            )
137            .child(
138                // Form
139                View::boxed()
140                    .flex(1)
141                    .border(true)
142                    .padding(1)
143                    .child(
144                        View::form()
145                            .spacing(1)
146                            .child(
147                                View::form_field("email")
148                                    .label("Email Address *")
149                                    .value(email.clone())
150                                    .placeholder("you@example.com")
151                                    .error(email_error)
152                                    .on_change(with!(form => move |v: String| {
153                                        form.get().set_value("email", v);
154                                    }))
155                                    .on_blur(with!(form => move || {
156                                        form.get().touch("email");
157                                    }))
158                                    .build(),
159                            )
160                            .child(
161                                View::form_field("username")
162                                    .label("Username *")
163                                    .value(username.clone())
164                                    .placeholder("johndoe")
165                                    .error(username_error)
166                                    .on_change(with!(form => move |v: String| {
167                                        form.get().set_value("username", v);
168                                    }))
169                                    .on_blur(with!(form => move || {
170                                        form.get().touch("username");
171                                    }))
172                                    .build(),
173                            )
174                            .child(
175                                View::form_field("password")
176                                    .label("Password * (min 8 chars)")
177                                    .value(password.clone())
178                                    .placeholder("Enter password")
179                                    .password(true)
180                                    .error(password_error)
181                                    .on_change(with!(form => move |v: String| {
182                                        form.get().set_value("password", v);
183                                    }))
184                                    .on_blur(with!(form => move || {
185                                        form.get().touch("password");
186                                    }))
187                                    .build(),
188                            )
189                            .child(
190                                View::form_field("age")
191                                    .label("Age (optional)")
192                                    .value(age.clone())
193                                    .placeholder("25")
194                                    .error(age_error)
195                                    .on_change(with!(form => move |v: String| {
196                                        form.get().set_value("age", v);
197                                    }))
198                                    .on_blur(with!(form => move || {
199                                        form.get().touch("age");
200                                    }))
201                                    .build(),
202                            )
203                            .build(),
204                    )
205                    .build(),
206            )
207            .child(
208                // Actions
209                View::hstack()
210                    .spacing(2)
211                    .child(View::button().label("Submit").on_press(on_submit).build())
212                    .child(View::button().label("Reset").on_press(on_reset).build())
213                    .build(),
214            )
215            .child(
216                // Status
217                View::boxed()
218                    .border(true)
219                    .padding(1)
220                    .child(
221                        View::vstack()
222                            .child(View::text(if submit_message.get().is_empty() {
223                                "Fill in the form and click Submit".to_string()
224                            } else {
225                                submit_message.get()
226                            }))
227                            .child(
228                                View::styled_text(format!(
229                                    "Form valid: {}",
230                                    if form.get().is_valid() { "Yes" } else { "No" }
231                                ))
232                                .color(if form.get().is_valid() {
233                                    Color::Green
234                                } else {
235                                    Color::Red
236                                })
237                                .build(),
238                            )
239                            .build(),
240                    )
241                    .build(),
242            )
243            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
244            .child(
245                View::modal()
246                    .visible(show_help.get())
247                    .title("Example 22: Forms")
248                    .on_dismiss(with!(show_help => move || show_help.set(false)))
249                    .child(
250                        View::vstack()
251                            .child(View::styled_text("What you're seeing").bold().build())
252                            .child(View::text("• Declarative form validation"))
253                            .child(View::text("• Required, email, length validators"))
254                            .child(View::text("• Custom validation functions"))
255                            .child(View::gap(1))
256                            .child(View::styled_text("Key concepts").bold().build())
257                            .child(View::text("• FormState manages all fields"))
258                            .child(View::text("• FieldBuilder defines validation"))
259                            .child(View::text("• View::form_field() renders inputs"))
260                            .child(View::text("• on_blur triggers validation"))
261                            .child(View::gap(1))
262                            .child(View::styled_text("Try this").bold().build())
263                            .child(View::text("• Enter invalid email, see error"))
264                            .child(View::text("• Try short password (<8 chars)"))
265                            .child(View::text("• Username with spaces shows error"))
266                            .child(View::gap(1))
267                            .child(View::styled_text("Next up").bold().build())
268                            .child(View::text("→ 23_modal: modal dialogs"))
269                            .child(View::gap(1))
270                            .child(View::styled_text("Press Escape to close").dim().build())
271                            .build(),
272                    )
273                    .build(),
274            )
275            .build()
276    }
Source

pub fn on_change(self, callback: impl Fn(String) + 'static) -> Self

Set the change callback.

Examples found in repository?
examples/22_forms.rs (lines 152-154)
21    fn render(&self, cx: Scope) -> View {
22        let show_help = state!(cx, || false);
23
24        // F1 toggles help
25        cx.use_command(
26            KeyBinding::key(KeyCode::F(1)),
27            with!(show_help => move || show_help.update(|v| *v = !*v)),
28        );
29
30        // Create form state with validated fields
31        let form = state!(cx, || {
32            FormState::new()
33                .field(
34                    FieldBuilder::new("email")
35                        .required()
36                        .email()
37                        .error_message("Please enter a valid email address")
38                        .build(),
39                )
40                .field(
41                    FieldBuilder::new("password")
42                        .required()
43                        .min_length(8)
44                        .error_message("Password must be at least 8 characters")
45                        .build(),
46                )
47                .field(
48                    FieldBuilder::new("username")
49                        .required()
50                        .min_length(3)
51                        .max_length(20)
52                        .custom(|v| {
53                            if v.contains(' ') {
54                                Some("Username cannot contain spaces".to_string())
55                            } else if !v.chars().all(|c| c.is_alphanumeric() || c == '_') {
56                                Some(
57                                    "Username can only contain letters, numbers, and underscores"
58                                        .to_string(),
59                                )
60                            } else {
61                                None
62                            }
63                        })
64                        .build(),
65                )
66                .field(
67                    FieldBuilder::new("age")
68                        .integer()
69                        .custom(|v| {
70                            if v.is_empty() {
71                                return None; // Optional field
72                            }
73                            match v.parse::<i32>() {
74                                Ok(age) if age < 0 => Some("Age cannot be negative".to_string()),
75                                Ok(age) if age > 150 => {
76                                    Some("Please enter a valid age".to_string())
77                                }
78                                Ok(_) => None,
79                                Err(_) => Some("Please enter a valid number".to_string()),
80                            }
81                        })
82                        .build(),
83                )
84        });
85
86        let submit_message = state!(cx, String::new);
87
88        // Get current field values and errors
89        let email = form.get().get_value("email");
90        let password = form.get().get_value("password");
91        let username = form.get().get_value("username");
92        let age = form.get().get_value("age");
93
94        let email_error = form.get().get_error("email");
95        let password_error = form.get().get_error("password");
96        let username_error = form.get().get_error("username");
97        let age_error = form.get().get_error("age");
98
99        // Handlers
100        let on_submit = with!(form, submit_message => move || {
101            if form.get().validate() {
102                let values = form.get().values();
103                submit_message.set(format!(
104                    "Form submitted! Email: {}, Username: {}",
105                    values.get("email").unwrap_or(&String::new()),
106                    values.get("username").unwrap_or(&String::new())
107                ));
108            } else {
109                submit_message.set("Please fix the errors above".to_string());
110            }
111        });
112
113        let on_reset = with!(form, submit_message => move || {
114            form.get().reset();
115            submit_message.set(String::new());
116        });
117
118        View::vstack()
119            .spacing(1)
120            .child(
121                // Header
122                View::boxed()
123                    .border(true)
124                    .padding(1)
125                    .child(
126                        View::vstack()
127                            .child(View::styled_text("Form Validation Demo").bold().build())
128                            .child(
129                                View::styled_text("Tab between fields, type to enter values")
130                                    .dim()
131                                    .build(),
132                            )
133                            .build(),
134                    )
135                    .build(),
136            )
137            .child(
138                // Form
139                View::boxed()
140                    .flex(1)
141                    .border(true)
142                    .padding(1)
143                    .child(
144                        View::form()
145                            .spacing(1)
146                            .child(
147                                View::form_field("email")
148                                    .label("Email Address *")
149                                    .value(email.clone())
150                                    .placeholder("you@example.com")
151                                    .error(email_error)
152                                    .on_change(with!(form => move |v: String| {
153                                        form.get().set_value("email", v);
154                                    }))
155                                    .on_blur(with!(form => move || {
156                                        form.get().touch("email");
157                                    }))
158                                    .build(),
159                            )
160                            .child(
161                                View::form_field("username")
162                                    .label("Username *")
163                                    .value(username.clone())
164                                    .placeholder("johndoe")
165                                    .error(username_error)
166                                    .on_change(with!(form => move |v: String| {
167                                        form.get().set_value("username", v);
168                                    }))
169                                    .on_blur(with!(form => move || {
170                                        form.get().touch("username");
171                                    }))
172                                    .build(),
173                            )
174                            .child(
175                                View::form_field("password")
176                                    .label("Password * (min 8 chars)")
177                                    .value(password.clone())
178                                    .placeholder("Enter password")
179                                    .password(true)
180                                    .error(password_error)
181                                    .on_change(with!(form => move |v: String| {
182                                        form.get().set_value("password", v);
183                                    }))
184                                    .on_blur(with!(form => move || {
185                                        form.get().touch("password");
186                                    }))
187                                    .build(),
188                            )
189                            .child(
190                                View::form_field("age")
191                                    .label("Age (optional)")
192                                    .value(age.clone())
193                                    .placeholder("25")
194                                    .error(age_error)
195                                    .on_change(with!(form => move |v: String| {
196                                        form.get().set_value("age", v);
197                                    }))
198                                    .on_blur(with!(form => move || {
199                                        form.get().touch("age");
200                                    }))
201                                    .build(),
202                            )
203                            .build(),
204                    )
205                    .build(),
206            )
207            .child(
208                // Actions
209                View::hstack()
210                    .spacing(2)
211                    .child(View::button().label("Submit").on_press(on_submit).build())
212                    .child(View::button().label("Reset").on_press(on_reset).build())
213                    .build(),
214            )
215            .child(
216                // Status
217                View::boxed()
218                    .border(true)
219                    .padding(1)
220                    .child(
221                        View::vstack()
222                            .child(View::text(if submit_message.get().is_empty() {
223                                "Fill in the form and click Submit".to_string()
224                            } else {
225                                submit_message.get()
226                            }))
227                            .child(
228                                View::styled_text(format!(
229                                    "Form valid: {}",
230                                    if form.get().is_valid() { "Yes" } else { "No" }
231                                ))
232                                .color(if form.get().is_valid() {
233                                    Color::Green
234                                } else {
235                                    Color::Red
236                                })
237                                .build(),
238                            )
239                            .build(),
240                    )
241                    .build(),
242            )
243            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
244            .child(
245                View::modal()
246                    .visible(show_help.get())
247                    .title("Example 22: Forms")
248                    .on_dismiss(with!(show_help => move || show_help.set(false)))
249                    .child(
250                        View::vstack()
251                            .child(View::styled_text("What you're seeing").bold().build())
252                            .child(View::text("• Declarative form validation"))
253                            .child(View::text("• Required, email, length validators"))
254                            .child(View::text("• Custom validation functions"))
255                            .child(View::gap(1))
256                            .child(View::styled_text("Key concepts").bold().build())
257                            .child(View::text("• FormState manages all fields"))
258                            .child(View::text("• FieldBuilder defines validation"))
259                            .child(View::text("• View::form_field() renders inputs"))
260                            .child(View::text("• on_blur triggers validation"))
261                            .child(View::gap(1))
262                            .child(View::styled_text("Try this").bold().build())
263                            .child(View::text("• Enter invalid email, see error"))
264                            .child(View::text("• Try short password (<8 chars)"))
265                            .child(View::text("• Username with spaces shows error"))
266                            .child(View::gap(1))
267                            .child(View::styled_text("Next up").bold().build())
268                            .child(View::text("→ 23_modal: modal dialogs"))
269                            .child(View::gap(1))
270                            .child(View::styled_text("Press Escape to close").dim().build())
271                            .build(),
272                    )
273                    .build(),
274            )
275            .build()
276    }
Source

pub fn on_blur(self, callback: impl Fn() + 'static) -> Self

Set the blur callback.

Examples found in repository?
examples/22_forms.rs (lines 155-157)
21    fn render(&self, cx: Scope) -> View {
22        let show_help = state!(cx, || false);
23
24        // F1 toggles help
25        cx.use_command(
26            KeyBinding::key(KeyCode::F(1)),
27            with!(show_help => move || show_help.update(|v| *v = !*v)),
28        );
29
30        // Create form state with validated fields
31        let form = state!(cx, || {
32            FormState::new()
33                .field(
34                    FieldBuilder::new("email")
35                        .required()
36                        .email()
37                        .error_message("Please enter a valid email address")
38                        .build(),
39                )
40                .field(
41                    FieldBuilder::new("password")
42                        .required()
43                        .min_length(8)
44                        .error_message("Password must be at least 8 characters")
45                        .build(),
46                )
47                .field(
48                    FieldBuilder::new("username")
49                        .required()
50                        .min_length(3)
51                        .max_length(20)
52                        .custom(|v| {
53                            if v.contains(' ') {
54                                Some("Username cannot contain spaces".to_string())
55                            } else if !v.chars().all(|c| c.is_alphanumeric() || c == '_') {
56                                Some(
57                                    "Username can only contain letters, numbers, and underscores"
58                                        .to_string(),
59                                )
60                            } else {
61                                None
62                            }
63                        })
64                        .build(),
65                )
66                .field(
67                    FieldBuilder::new("age")
68                        .integer()
69                        .custom(|v| {
70                            if v.is_empty() {
71                                return None; // Optional field
72                            }
73                            match v.parse::<i32>() {
74                                Ok(age) if age < 0 => Some("Age cannot be negative".to_string()),
75                                Ok(age) if age > 150 => {
76                                    Some("Please enter a valid age".to_string())
77                                }
78                                Ok(_) => None,
79                                Err(_) => Some("Please enter a valid number".to_string()),
80                            }
81                        })
82                        .build(),
83                )
84        });
85
86        let submit_message = state!(cx, String::new);
87
88        // Get current field values and errors
89        let email = form.get().get_value("email");
90        let password = form.get().get_value("password");
91        let username = form.get().get_value("username");
92        let age = form.get().get_value("age");
93
94        let email_error = form.get().get_error("email");
95        let password_error = form.get().get_error("password");
96        let username_error = form.get().get_error("username");
97        let age_error = form.get().get_error("age");
98
99        // Handlers
100        let on_submit = with!(form, submit_message => move || {
101            if form.get().validate() {
102                let values = form.get().values();
103                submit_message.set(format!(
104                    "Form submitted! Email: {}, Username: {}",
105                    values.get("email").unwrap_or(&String::new()),
106                    values.get("username").unwrap_or(&String::new())
107                ));
108            } else {
109                submit_message.set("Please fix the errors above".to_string());
110            }
111        });
112
113        let on_reset = with!(form, submit_message => move || {
114            form.get().reset();
115            submit_message.set(String::new());
116        });
117
118        View::vstack()
119            .spacing(1)
120            .child(
121                // Header
122                View::boxed()
123                    .border(true)
124                    .padding(1)
125                    .child(
126                        View::vstack()
127                            .child(View::styled_text("Form Validation Demo").bold().build())
128                            .child(
129                                View::styled_text("Tab between fields, type to enter values")
130                                    .dim()
131                                    .build(),
132                            )
133                            .build(),
134                    )
135                    .build(),
136            )
137            .child(
138                // Form
139                View::boxed()
140                    .flex(1)
141                    .border(true)
142                    .padding(1)
143                    .child(
144                        View::form()
145                            .spacing(1)
146                            .child(
147                                View::form_field("email")
148                                    .label("Email Address *")
149                                    .value(email.clone())
150                                    .placeholder("you@example.com")
151                                    .error(email_error)
152                                    .on_change(with!(form => move |v: String| {
153                                        form.get().set_value("email", v);
154                                    }))
155                                    .on_blur(with!(form => move || {
156                                        form.get().touch("email");
157                                    }))
158                                    .build(),
159                            )
160                            .child(
161                                View::form_field("username")
162                                    .label("Username *")
163                                    .value(username.clone())
164                                    .placeholder("johndoe")
165                                    .error(username_error)
166                                    .on_change(with!(form => move |v: String| {
167                                        form.get().set_value("username", v);
168                                    }))
169                                    .on_blur(with!(form => move || {
170                                        form.get().touch("username");
171                                    }))
172                                    .build(),
173                            )
174                            .child(
175                                View::form_field("password")
176                                    .label("Password * (min 8 chars)")
177                                    .value(password.clone())
178                                    .placeholder("Enter password")
179                                    .password(true)
180                                    .error(password_error)
181                                    .on_change(with!(form => move |v: String| {
182                                        form.get().set_value("password", v);
183                                    }))
184                                    .on_blur(with!(form => move || {
185                                        form.get().touch("password");
186                                    }))
187                                    .build(),
188                            )
189                            .child(
190                                View::form_field("age")
191                                    .label("Age (optional)")
192                                    .value(age.clone())
193                                    .placeholder("25")
194                                    .error(age_error)
195                                    .on_change(with!(form => move |v: String| {
196                                        form.get().set_value("age", v);
197                                    }))
198                                    .on_blur(with!(form => move || {
199                                        form.get().touch("age");
200                                    }))
201                                    .build(),
202                            )
203                            .build(),
204                    )
205                    .build(),
206            )
207            .child(
208                // Actions
209                View::hstack()
210                    .spacing(2)
211                    .child(View::button().label("Submit").on_press(on_submit).build())
212                    .child(View::button().label("Reset").on_press(on_reset).build())
213                    .build(),
214            )
215            .child(
216                // Status
217                View::boxed()
218                    .border(true)
219                    .padding(1)
220                    .child(
221                        View::vstack()
222                            .child(View::text(if submit_message.get().is_empty() {
223                                "Fill in the form and click Submit".to_string()
224                            } else {
225                                submit_message.get()
226                            }))
227                            .child(
228                                View::styled_text(format!(
229                                    "Form valid: {}",
230                                    if form.get().is_valid() { "Yes" } else { "No" }
231                                ))
232                                .color(if form.get().is_valid() {
233                                    Color::Green
234                                } else {
235                                    Color::Red
236                                })
237                                .build(),
238                            )
239                            .build(),
240                    )
241                    .build(),
242            )
243            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
244            .child(
245                View::modal()
246                    .visible(show_help.get())
247                    .title("Example 22: Forms")
248                    .on_dismiss(with!(show_help => move || show_help.set(false)))
249                    .child(
250                        View::vstack()
251                            .child(View::styled_text("What you're seeing").bold().build())
252                            .child(View::text("• Declarative form validation"))
253                            .child(View::text("• Required, email, length validators"))
254                            .child(View::text("• Custom validation functions"))
255                            .child(View::gap(1))
256                            .child(View::styled_text("Key concepts").bold().build())
257                            .child(View::text("• FormState manages all fields"))
258                            .child(View::text("• FieldBuilder defines validation"))
259                            .child(View::text("• View::form_field() renders inputs"))
260                            .child(View::text("• on_blur triggers validation"))
261                            .child(View::gap(1))
262                            .child(View::styled_text("Try this").bold().build())
263                            .child(View::text("• Enter invalid email, see error"))
264                            .child(View::text("• Try short password (<8 chars)"))
265                            .child(View::text("• Username with spaces shows error"))
266                            .child(View::gap(1))
267                            .child(View::styled_text("Next up").bold().build())
268                            .child(View::text("→ 23_modal: modal dialogs"))
269                            .child(View::gap(1))
270                            .child(View::styled_text("Press Escape to close").dim().build())
271                            .build(),
272                    )
273                    .build(),
274            )
275            .build()
276    }
Source

pub fn cursor(self, pos: usize) -> Self

Set the cursor position.

Source

pub fn build(self) -> View

Examples found in repository?
examples/22_forms.rs (line 158)
21    fn render(&self, cx: Scope) -> View {
22        let show_help = state!(cx, || false);
23
24        // F1 toggles help
25        cx.use_command(
26            KeyBinding::key(KeyCode::F(1)),
27            with!(show_help => move || show_help.update(|v| *v = !*v)),
28        );
29
30        // Create form state with validated fields
31        let form = state!(cx, || {
32            FormState::new()
33                .field(
34                    FieldBuilder::new("email")
35                        .required()
36                        .email()
37                        .error_message("Please enter a valid email address")
38                        .build(),
39                )
40                .field(
41                    FieldBuilder::new("password")
42                        .required()
43                        .min_length(8)
44                        .error_message("Password must be at least 8 characters")
45                        .build(),
46                )
47                .field(
48                    FieldBuilder::new("username")
49                        .required()
50                        .min_length(3)
51                        .max_length(20)
52                        .custom(|v| {
53                            if v.contains(' ') {
54                                Some("Username cannot contain spaces".to_string())
55                            } else if !v.chars().all(|c| c.is_alphanumeric() || c == '_') {
56                                Some(
57                                    "Username can only contain letters, numbers, and underscores"
58                                        .to_string(),
59                                )
60                            } else {
61                                None
62                            }
63                        })
64                        .build(),
65                )
66                .field(
67                    FieldBuilder::new("age")
68                        .integer()
69                        .custom(|v| {
70                            if v.is_empty() {
71                                return None; // Optional field
72                            }
73                            match v.parse::<i32>() {
74                                Ok(age) if age < 0 => Some("Age cannot be negative".to_string()),
75                                Ok(age) if age > 150 => {
76                                    Some("Please enter a valid age".to_string())
77                                }
78                                Ok(_) => None,
79                                Err(_) => Some("Please enter a valid number".to_string()),
80                            }
81                        })
82                        .build(),
83                )
84        });
85
86        let submit_message = state!(cx, String::new);
87
88        // Get current field values and errors
89        let email = form.get().get_value("email");
90        let password = form.get().get_value("password");
91        let username = form.get().get_value("username");
92        let age = form.get().get_value("age");
93
94        let email_error = form.get().get_error("email");
95        let password_error = form.get().get_error("password");
96        let username_error = form.get().get_error("username");
97        let age_error = form.get().get_error("age");
98
99        // Handlers
100        let on_submit = with!(form, submit_message => move || {
101            if form.get().validate() {
102                let values = form.get().values();
103                submit_message.set(format!(
104                    "Form submitted! Email: {}, Username: {}",
105                    values.get("email").unwrap_or(&String::new()),
106                    values.get("username").unwrap_or(&String::new())
107                ));
108            } else {
109                submit_message.set("Please fix the errors above".to_string());
110            }
111        });
112
113        let on_reset = with!(form, submit_message => move || {
114            form.get().reset();
115            submit_message.set(String::new());
116        });
117
118        View::vstack()
119            .spacing(1)
120            .child(
121                // Header
122                View::boxed()
123                    .border(true)
124                    .padding(1)
125                    .child(
126                        View::vstack()
127                            .child(View::styled_text("Form Validation Demo").bold().build())
128                            .child(
129                                View::styled_text("Tab between fields, type to enter values")
130                                    .dim()
131                                    .build(),
132                            )
133                            .build(),
134                    )
135                    .build(),
136            )
137            .child(
138                // Form
139                View::boxed()
140                    .flex(1)
141                    .border(true)
142                    .padding(1)
143                    .child(
144                        View::form()
145                            .spacing(1)
146                            .child(
147                                View::form_field("email")
148                                    .label("Email Address *")
149                                    .value(email.clone())
150                                    .placeholder("you@example.com")
151                                    .error(email_error)
152                                    .on_change(with!(form => move |v: String| {
153                                        form.get().set_value("email", v);
154                                    }))
155                                    .on_blur(with!(form => move || {
156                                        form.get().touch("email");
157                                    }))
158                                    .build(),
159                            )
160                            .child(
161                                View::form_field("username")
162                                    .label("Username *")
163                                    .value(username.clone())
164                                    .placeholder("johndoe")
165                                    .error(username_error)
166                                    .on_change(with!(form => move |v: String| {
167                                        form.get().set_value("username", v);
168                                    }))
169                                    .on_blur(with!(form => move || {
170                                        form.get().touch("username");
171                                    }))
172                                    .build(),
173                            )
174                            .child(
175                                View::form_field("password")
176                                    .label("Password * (min 8 chars)")
177                                    .value(password.clone())
178                                    .placeholder("Enter password")
179                                    .password(true)
180                                    .error(password_error)
181                                    .on_change(with!(form => move |v: String| {
182                                        form.get().set_value("password", v);
183                                    }))
184                                    .on_blur(with!(form => move || {
185                                        form.get().touch("password");
186                                    }))
187                                    .build(),
188                            )
189                            .child(
190                                View::form_field("age")
191                                    .label("Age (optional)")
192                                    .value(age.clone())
193                                    .placeholder("25")
194                                    .error(age_error)
195                                    .on_change(with!(form => move |v: String| {
196                                        form.get().set_value("age", v);
197                                    }))
198                                    .on_blur(with!(form => move || {
199                                        form.get().touch("age");
200                                    }))
201                                    .build(),
202                            )
203                            .build(),
204                    )
205                    .build(),
206            )
207            .child(
208                // Actions
209                View::hstack()
210                    .spacing(2)
211                    .child(View::button().label("Submit").on_press(on_submit).build())
212                    .child(View::button().label("Reset").on_press(on_reset).build())
213                    .build(),
214            )
215            .child(
216                // Status
217                View::boxed()
218                    .border(true)
219                    .padding(1)
220                    .child(
221                        View::vstack()
222                            .child(View::text(if submit_message.get().is_empty() {
223                                "Fill in the form and click Submit".to_string()
224                            } else {
225                                submit_message.get()
226                            }))
227                            .child(
228                                View::styled_text(format!(
229                                    "Form valid: {}",
230                                    if form.get().is_valid() { "Yes" } else { "No" }
231                                ))
232                                .color(if form.get().is_valid() {
233                                    Color::Green
234                                } else {
235                                    Color::Red
236                                })
237                                .build(),
238                            )
239                            .build(),
240                    )
241                    .build(),
242            )
243            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
244            .child(
245                View::modal()
246                    .visible(show_help.get())
247                    .title("Example 22: Forms")
248                    .on_dismiss(with!(show_help => move || show_help.set(false)))
249                    .child(
250                        View::vstack()
251                            .child(View::styled_text("What you're seeing").bold().build())
252                            .child(View::text("• Declarative form validation"))
253                            .child(View::text("• Required, email, length validators"))
254                            .child(View::text("• Custom validation functions"))
255                            .child(View::gap(1))
256                            .child(View::styled_text("Key concepts").bold().build())
257                            .child(View::text("• FormState manages all fields"))
258                            .child(View::text("• FieldBuilder defines validation"))
259                            .child(View::text("• View::form_field() renders inputs"))
260                            .child(View::text("• on_blur triggers validation"))
261                            .child(View::gap(1))
262                            .child(View::styled_text("Try this").bold().build())
263                            .child(View::text("• Enter invalid email, see error"))
264                            .child(View::text("• Try short password (<8 chars)"))
265                            .child(View::text("• Username with spaces shows error"))
266                            .child(View::gap(1))
267                            .child(View::styled_text("Next up").bold().build())
268                            .child(View::text("→ 23_modal: modal dialogs"))
269                            .child(View::gap(1))
270                            .child(View::styled_text("Press Escape to close").dim().build())
271                            .build(),
272                    )
273                    .build(),
274            )
275            .build()
276    }

Trait Implementations§

Source§

impl Default for FormFieldBuilder

Source§

fn default() -> FormFieldBuilder

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.