Aria 2.0.0
C++23 MVVM framework (C++20 minimum) — reactive, coroutine-first, ABI-layered
Loading...
Searching...
No Matches
Validation

Aria's validation system is reactive-first: validators bind to Property<T> and automatically re-evaluate when the value changes. Results are exposed as Property<ValidationState> — bind them to your UI like any other reactive value.

Include: #include "aria/validator.hpp"


Validator<T>

Attaches validation rules to a Property<T>. Re-runs all rules whenever the source changes.

Basic: Required Field

aria::Validator<std::string> email_valid{email, "email"};
email_valid.must([](const std::string& v) { return !v.empty(); },
"Email is required", "required");
email_valid.state().bind([](const aria::ValidationState& s) {
if (!s.valid) {
show_error(s.first_error_message().value_or(""));
}
});
Definition validator.hpp:135
Property< ValidationState > & state() noexcept
Definition validator.hpp:312
Validator & must(P &&predicate, std::string message, std::string rule_id_value={})
Definition validator.hpp:184
Definition property.hpp:103
Definition validator.hpp:61
std::optional< std::string > first_error_message() const
First Error-severity message text, if any.
Definition validator.hpp:93
bool valid
True iff there are no Error-severity entries.
Definition validator.hpp:64

Multiple Rules

email_valid
.must([](const std::string& v) { return !v.empty(); },
"Email is required", "required")
.must([](const std::string& v) { return v.contains('@'); },
"Must contain @", "at_sign")
.must([](const std::string& v) { return v.size() >= 5; },
"Too short", "min_length");

Rules are evaluated in order. All failing rules contribute errors — the user sees every problem at once.

Warnings (Soft Advisories)

Warnings do NOT make the field invalid:

email_valid.should([](const std::string& v) { return !v.ends_with(".co"); },
".co domains may have issues", "dot_co_warning");
Validator & should(P &&predicate, std::string message, std::string rule_id_value={})
Definition validator.hpp:206

Custom Rule Function

Full control — return std::optional<std::string> (message on failure, nullopt on pass):

email_valid.rule([](const std::string& v) -> std::optional<std::string> {
if (v.find("..") != std::string::npos)
return "Consecutive dots are not allowed";
return std::nullopt;
}, "no_double_dots");
Validator & rule(Rule r, std::string rule_id_value={})
Definition validator.hpp:173

Bulk Rules

Add multiple rules without triggering intermediate evaluations:

email_valid.rules({
[](const std::string& v) -> std::optional<std::string> {
return v.empty() ? std::optional{"Required"} : std::nullopt;
},
[](const std::string& v) -> std::optional<std::string> {
return !v.contains('@') ? std::optional{"Need @"} : std::nullopt;
}
});
Validator & rules(std::initializer_list< Rule > rs)
Definition validator.hpp:217

ValidationState

The rich form-state record exposed by validator.state():

Field Type Meaning
valid bool True if no Error-severity entries
pending bool True while async validation is running
touched bool True after user interaction (focus-out)
dirty bool True if value differs from baseline
errors vector<Error> Hard failures
warnings vector<Error> Soft advisories

Convenience Queries

auto& s = validator.state().get();
s.first_error(); // optional<Error>
s.first_error_message(); // optional<string>
s.errors_for("email"); // errors targeting a specific field
s.has_error_with_rule("required"); // bool
std::optional< Error > first_error() const
First Error-severity entry, if any.
Definition validator.hpp:87
std::vector< Error > errors_for(std::string_view field_path) const
All Error-severity entries that target a given field path.
Definition validator.hpp:100
bool has_error_with_rule(std::string_view rule_id) const noexcept
True if any Error-severity entry has the given rule_id.
Definition validator.hpp:109

Form-State Transitions

Touch

Mark a field as user-interacted (typically on blur):

email_valid.touch();
// state().touched → true
void touch()
Definition validator.hpp:230

Reset Dirty

Snap the baseline to the current value (e.g. after a successful save):

email_valid.reset_dirty();
// state().dirty → false
void reset_dirty()
Definition validator.hpp:244

Reset Touched

Clear the touched flag (e.g. when resetting a form):

email_valid.reset_touched();
// state().touched → false
void reset_touched()
Definition validator.hpp:237

Async Validation

For rules that need server-side checks (username availability, etc.):

aria::Validator<std::string> username_valid{username, "username"};
username_valid.must([](const std::string& v) { return v.size() >= 3; },
"At least 3 characters", "min_length");
// In your async handler:
username_valid.begin_pending(); // state().pending → true
// ... later, when server responds ...
username_valid.end_pending({"Username taken"}); // pending → false, adds async errors
// Or:
username_valid.end_pending(); // pending → false, no extra errors
void begin_pending()
Definition validator.hpp:256
void end_pending(std::vector< std::string > extra_messages)
Settle the async validation: clears pending and stores extra_messages as additional Error-severity en...
Definition validator.hpp:276

ValidationResult (Legacy)

A simpler projection with just valid + errors. Available via validator.result():

auto& r = email_valid.result().get();
r.valid; // bool
r.errors; // vector<Error>
r.error_messages(); // vector<string>
Property< ValidationResult > & result() noexcept
Definition validator.hpp:315

New code should prefer state() — it includes pending, touched, and dirty.


FormValidator

Compose multiple validators into a form-level validity check:

form.add(email_valid);
form.add(password_valid);
form.add(age_valid);
form.state().bind([](const aria::ValidationState& s) {
submit_button.setEnabled(s.valid && !s.pending);
});
Definition form.hpp:171
bool pending
True while an async validator is running.
Definition validator.hpp:67

Quick Reference

Method Description
Validator(prop, field_path) Attach to a Property
.rule(fn, id) Add custom rule
.must(pred, msg, id) Add predicate rule (error)
.should(pred, msg, id) Add predicate rule (warning)
.rules({...}) Bulk-add rules
.touch() Mark as user-interacted
.reset_touched() Clear touched flag
.reset_dirty() Snap baseline to current
.begin_pending() Enter async-validation state
.end_pending(msgs) Exit pending, add async errors
.state() Property<ValidationState>&
.result() Property<ValidationResult>&

See Also