Forms — widgets and support classes for capturing user information

Validation

A validator is used to validate user input according to pre-defined rules.

A validator may have a split implementation to provide both validation at the client-side (which gives instant feed-back to the user while editing), and server-side validation (to be sure that the client was not tampered with). The feed-back given by (client-side and server-side) validation is reflected in the style class of the form field: a style class of Wt-invalid is set for a field that is invalid.

The WValidator only checks that mandatory fields are not empty. Validated input can have one of the following states:

  • Valid, if the input data is valid.
  • Invalid, if the input data is invalid.
  • InvalidEmpty, if the input field is empty while it is configured as mandatory.
This class is reimplemented in WDateValidator, WIntValidator, WDoubleValidator, WLengthValidator and WRegExpValidator. All these validators provide both client-side and server-side validation.

If these validators are not suitable, you can inherit from this class, and provide a suitable implementation to validate() which does the server-side validation. If you want to provide client-side validation for your own validator, you may also reimplement javaScriptValidate() .

Example
source
#include <Wt/WIntValidator.h>
#include <Wt/WLineEdit.h>
#include <Wt/WPushButton.h>
#include <Wt/WTemplate.h>
#include <Wt/WText.h>


auto t = Wt::cpp14::make_unique<Wt::WTemplate>(Wt::WString::tr("validation-template"));
t->addFunction("id", &Wt::WTemplate::Functions::id);

auto ageEdit = t->bindWidget("age", Wt::cpp14::make_unique<Wt::WLineEdit>());

auto validator = std::make_shared<Wt::WIntValidator>(0, 150);
validator->setMandatory(true);
ageEdit->setValidator(validator);

auto button = t->bindWidget("button", Wt::cpp14::make_unique<Wt::WPushButton>("Save"));

auto out = t->bindWidget("age-info", Wt::cpp14::make_unique<Wt::WText>());
out->setInline(false);
out->hide();

button->clicked().connect([=] {
    out->show();
    if (ageEdit->validate() == Wt::ValidationState::Valid) {
        out->setText("Age of " + ageEdit->text() + " is saved!");
        out->setStyleClass("alert alert-success");
    } else {
        out->setText("The number must be in the range 0 to 150");
        out->setStyleClass("alert alert-danger");
    }
});

ageEdit->enterPressed().connect([=] {
    button->clicked().emit(Wt::WMouseEvent());
});

Here is the corresponding XML template (with message id= "validation-template") using style classes from the Bootstrap theme. In order to apply validation style if the input is not valid, you should surround the input widget in a div or span section and apply the style form-group to it.

source
<div class="form-horizontal">
<div class="form-group">
<label class="control-label col-sm-3" for="${id:age}">
Please enter your age:
</label>
<div class="col-sm-2">${age}</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-7">
${button}
</div>
</div>
${age-info}
</div>

Top

Date validation

In entirely the same way, a date validator can be used to validate date input with a WDateEdit or WDatePicker. You can limit the date range of the calendar using setBottom() and setTop().

Example
source
#include <Wt/WTemplate.h>
#include <Wt/WDate.h>
#include <Wt/WDateValidator.h>
#include <Wt/WLabel.h>
#include <Wt/WDateEdit.h>
#include <Wt/WPushButton.h>
#include <Wt/WValidator.h>


auto t = Wt::cpp14::make_unique<Wt::WTemplate>(Wt::WString::tr("date-template"));
t->addFunction("id", &Wt::WTemplate::Functions::id);

auto dateEdit = t->bindWidget("birth-date", Wt::cpp14::make_unique<Wt::WDateEdit>());

auto dv = std::make_shared<Wt::WDateValidator>();
dv->setBottom(Wt::WDate(1900, 1, 1));
dv->setTop(Wt::WDate::currentDate());
dv->setFormat("dd/MM/yyyy");
dv->setMandatory(true);
dv->setInvalidBlankText("A birthdate is mandatory!");
dv->setInvalidNotADateText("You should enter a date in the format "
			   "\"dd/MM/yyyy\"!");
dv->setInvalidTooEarlyText
    (Wt::WString("That's too early... The date must be {1} or later!"
		 "").arg(dv->bottom().toString("dd/MM/yyyy")));
dv->setInvalidTooLateText
    (Wt::WString("That's too late... The date must be {1} or earlier!"
		 "").arg(dv->top().toString("dd/MM/yyyy")));

dateEdit->setValidator(dv);

auto button = t->bindWidget("button", Wt::cpp14::make_unique<Wt::WPushButton>("Ok"));

auto out = t->bindWidget("info", Wt::cpp14::make_unique<Wt::WText>());
out->setInline(false);
out->hide();

button->clicked().connect([=] {
    out->show();

    Wt::WValidator::Result result = dv->validate(dateEdit->text());
    if (result.state() == Wt::ValidationState::Valid) {
        Wt::WDate d = Wt::WDate::currentServerDate();
        int years = d.year() - dateEdit->date().year();
        int days = d.daysTo(dateEdit->date().addYears(years));
	if (days < 0)
	    days = d.daysTo( dateEdit->date().addYears(years + 1) );
	out->setText("<p>In " + std::to_string(days) +
		     " days, we will be celebrating your next anniversary!</p>");
	out->setStyleClass("alert alert-success");
    } else {
        dateEdit->setFocus(true);
        out->setText(result.message());
        out->setStyleClass("alert alert-danger");
    }
});

dateEdit->enterPressed().connect([=] {
    button->clicked().emit(Wt::WMouseEvent());
});

Top

Server-side validation using a form model

A more convenient way to perform validation is to implement a form model (a specialisation of WFormModel) and a complementary template-based view (a specialisation of WTemplateFormView). In this way you can validate the values entered at the server-side.

The form model implements field data and validation handling for (simple) form-based views. It provides a standard way for views to perform field validation, and react to validation results. Each field has a string literal assigned to it. The string literal uniquely identifies the field. For each field, its value, the visibility, whether the field is read-only, and its current validation status is managed by the model. In addition, you will typically specialize the class to customize the validation and application logic. Although it can be setup to use WValidator objects for individual fields, you could also create a validator to simultaneously validate interdependent fields.

A model is typically used by a view which renders the fields configured in the model, updates the model values, invokes and reflects the validation status. See Forms for more details on implementing forms.

Here is an example with the same behavior as the previous example. It also uses the same XML template. The contents of the variable name 'age-info' in the template depends on the outcome of the method validate() of the model.

  • It holds a confirmation that the value is saved if the input is valid.
  • It holds a validation message if the input is not valid.

Example
source
#include <Wt/WFormModel.h>
#include <Wt/WIntValidator.h>
#include <Wt/WLineEdit.h>
#include <Wt/WPushButton.h>
#include <Wt/WValidator.h>
#include <Wt/WTemplateFormView.h>

class AgeFormModel : public Wt::WFormModel
{
public:
    static const Field AgeField;

    // inline constructor
    AgeFormModel() : WFormModel()
    {
        addField(AgeField);
        setValidator(AgeField, createAgeValidator());
        setValue(AgeField, std::string());
    }

private:
    std::shared_ptr<Wt::WValidator> createAgeValidator() {
        return std::make_shared<Wt::WIntValidator>(0, 150);
    }
};

const Wt::WFormModel::Field AgeFormModel::AgeField = "age";

class AgeFormView : public Wt::WTemplateFormView
{
public:
    // inline constructor
    AgeFormView() {
        model_ = Wt::cpp14::make_unique<AgeFormModel>();

        setTemplateText(tr("validation-template"));

        setFormWidget(AgeFormModel::AgeField, Wt::cpp14::make_unique<Wt::WLineEdit>());

        auto button = bindWidget("button",
                                 Wt::cpp14::make_unique<Wt::WPushButton>("Save"));

        button->clicked().connect(this, &AgeFormView::process);

        updateView(model_.get());
    }

private:
    void process() {
        updateModel(model_.get());
        if (model_->validate()) {
            // Udate the view: Delete any validation message in the view, etc.
            updateView(model_.get());
            bindString("age-info",
                       Wt::WString("Age of {1} is saved!")
                       .arg(Wt::asString(model_->value(AgeFormModel::AgeField))));
        } else {
            updateView(model_.get());
            // Set the focus on the line edit.
            Wt::WLineEdit *viewField =
                       resolve<Wt::WLineEdit *>(AgeFormModel::AgeField);
            viewField->setFocus(true);
        }
    }

    std::unique_ptr<AgeFormModel> model_;
};


auto view = Wt::cpp14::make_unique<AgeFormView>();

Top