#include <Wt/WApplication.h>
#include <Wt/WAny.h>
#include <Wt/WComboBox.h>
#include <Wt/WDate.h>
#include <Wt/WDateEdit.h>
#include <Wt/WDateValidator.h>
#include <Wt/WFormModel.h>
#include <Wt/WImage.h>
#include <Wt/WIntValidator.h>
#include <Wt/WLengthValidator.h>
#include <Wt/WModelIndex.h>
#include <Wt/WPushButton.h>
#include <Wt/WSpinBox.h>
#include <Wt/WStandardItem.h>
#include <Wt/WStandardItemModel.h>
#include <Wt/WString.h>
#include <Wt/WTemplateFormView.h>
#include <Wt/WTextArea.h>
#include <Wt/WValidator.h>
class UserFormModel : public Wt::WFormModel
{
public:
// Associate each field with a unique string literal.
static const Field FirstNameField;
static const Field LastNameField;
static const Field CountryField;
static const Field CityField;
static const Field BirthField;
static const Field ChildrenField;
static const Field RemarksField;
UserFormModel()
: WFormModel()
{
initializeModels();
addField(FirstNameField);
addField(LastNameField);
addField(CountryField);
addField(CityField);
addField(BirthField);
addField(ChildrenField);
addField(RemarksField);
setValidator(FirstNameField, createNameValidator(FirstNameField));
setValidator(LastNameField, createNameValidator(LastNameField));
setValidator(CountryField, createCountryValidator());
setValidator(CityField, createCityValidator());
setValidator(BirthField, createBirthValidator());
setValidator(ChildrenField, createChildrenValidator());
// Here you could populate the model with initial data using
// setValue() for each field.
setValue(BirthField, Wt::WDate());
setValue(CountryField, std::string());
}
std::shared_ptr<Wt::WAbstractItemModel> countryModel() {
return countryModel_;
}
int countryModelRow(const std::string& code) {
for (int i = 0; i < countryModel_->rowCount(); ++i)
if (countryCode(i) == code)
return i;
return -1;
}
std::shared_ptr<Wt::WAbstractItemModel> cityModel() {
return cityModel_;
}
void updateCityModel(const std::string& countryCode) {
cityModel_->clear();
CityMap::const_iterator i = cities.find(countryCode);
if (i != cities.end()) {
const std::vector<std::string>& cities = i->second;
// The initial text shown in the city combo box should be an empty
// string.
cityModel_->appendRow(std::make_unique<Wt::WStandardItem>());
for (unsigned j = 0; j < cities.size(); ++j)
cityModel_->appendRow(std::make_unique<Wt::WStandardItem>(cities[j]));
} else {
cityModel_->appendRow(
std::make_unique<Wt::WStandardItem>("(Choose Country first)"));
}
}
// Get the user data from the model
Wt::WString userData() {
return
Wt::asString(value(FirstNameField)) + " " +
Wt::asString(value(LastNameField))
+ ": country code=" + Wt::asString(value(CountryField))
+ ", city=" + Wt::asString(value(CityField))
+ ", birth=" + Wt::asString(value(BirthField))
+ ", children=" + Wt::asString(value(ChildrenField))
+ ", remarks=" + Wt::asString(value(RemarksField))
+ ".";
}
// Get the right code for the current index.
std::string countryCode (int row) {
return Wt::asString(countryModel_->data(row, 0, Wt::ItemDataRole::User)).toUTF8();
}
typedef std::map< std::string, std::vector<std::string> > CityMap;
typedef std::map<std::string, std::string> CountryMap;
private:
static const CityMap cities;
static const CountryMap countries;
std::shared_ptr<Wt::WStandardItemModel> countryModel_, cityModel_;
static const int MAX_LENGTH;
static const int MAX_CHILDREN;
void initializeModels() {
// Create a country model.
unsigned countryModelRows = countries.size() + 1;
const unsigned countryModelColumns = 1;
countryModel_ =
std::make_shared<Wt::WStandardItemModel>(countryModelRows, countryModelColumns);
// The initial text shown in the country combo box should be an empty
// string.
int row = 0;
countryModel_->setData(row, 0, std::string(" "), Wt::ItemDataRole::Display);
countryModel_->setData(row, 0, std::string(), Wt::ItemDataRole::User);
// For each country, update the model based on the key (corresponding
// to the country code):
// - set the country name for the display role,
// - set the city names for the user role.
row = 1;
for (CountryMap::const_iterator i = countries.begin();
i != countries.end(); ++i) {
countryModel_->setData(row, 0, i->second, Wt::ItemDataRole::Display);
countryModel_->setData(row++, 0, i->first, Wt::ItemDataRole::User);
}
// Create a city model.
cityModel_ = std::make_shared<Wt::WStandardItemModel>();
updateCityModel(std::string());
}
std::shared_ptr<Wt::WValidator> createNameValidator(const std::string& field) {
auto v = std::make_shared<Wt::WLengthValidator>();
v->setMandatory(true);
v->setMinimumLength(1);
v->setMaximumLength(MAX_LENGTH);
return v;
}
std::shared_ptr<Wt::WValidator> createCountryValidator() {
auto v = std::make_shared<Wt::WLengthValidator>();
v->setMandatory(true);
return v;
}
std::shared_ptr<Wt::WValidator> createCityValidator() {
auto v = std::make_shared<Wt::WLengthValidator>();
v->setMandatory(true);
return v;
}
std::shared_ptr<Wt::WValidator> createBirthValidator() {
auto v = std::make_shared<Wt::WDateValidator>();
v->setBottom(Wt::WDate(1900, 1, 1));
v->setTop(Wt::WDate::currentDate());
v->setFormat("dd/MM/yyyy");
v->setMandatory(true);
return v;
}
std::shared_ptr<Wt::WValidator> createChildrenValidator() {
auto v = std::make_shared<Wt::WIntValidator>(0, MAX_CHILDREN);
v->setMandatory(true);
return v;
}
};
const int UserFormModel::MAX_LENGTH = 25;
const int UserFormModel::MAX_CHILDREN = 15;
const Wt::WFormModel::Field UserFormModel::FirstNameField = "first-name";
const Wt::WFormModel::Field UserFormModel::LastNameField = "last-name";
const Wt::WFormModel::Field UserFormModel::CountryField = "country";
const Wt::WFormModel::Field UserFormModel::CityField = "city";
const Wt::WFormModel::Field UserFormModel::BirthField = "birth";
const Wt::WFormModel::Field UserFormModel::ChildrenField = "children";
const Wt::WFormModel::Field UserFormModel::RemarksField = "remarks";
const UserFormModel::CountryMap UserFormModel::countries = {
{ "BE", { "Belgium" } },
{ "NL", { "Netherlands" } },
{ "UK", { "United Kingdom" } },
{ "US", { "United States" } }
};
const UserFormModel::CityMap UserFormModel::cities = {
{ "BE", { "Antwerp", "Bruges", "Brussels", "Ghent" } },
{ "NL", { "Amsterdam", "Eindhoven", "Rotterdam", "The Hague"} },
{ "UK", { "London", "Bristol", "Oxford", "Stonehenge"} },
{ "US", { "Boston", "Chicago", "Los Angeles", "New York"} }
};
class UserFormView : public Wt::WTemplateFormView
{
public:
// inline constructor
UserFormView() {
model = std::make_shared<UserFormModel>();
setTemplateText(tr("userForm-template"));
addFunction("id", &WTemplate::Functions::id);
addFunction("block", &WTemplate::Functions::id);
/*
* First Name
*/
setFormWidget(UserFormModel::FirstNameField,
std::make_unique<Wt::WLineEdit>());
/*
* Last Name
*/
setFormWidget(UserFormModel::LastNameField,
std::make_unique<Wt::WLineEdit>());
/*
* Country
*/
auto countryCB = std::make_unique<Wt::WComboBox>();
auto countryCB_ = countryCB.get();
countryCB->setModel(model->countryModel());
countryCB_->activated().connect([=] {
std::string code = model->countryCode(countryCB_->currentIndex());
model->updateCityModel(code);
});
setFormWidget(UserFormModel::CountryField, std::move(countryCB),
[=] { // updateViewValue()
std::string code =
Wt::asString(model->value(UserFormModel::CountryField)).toUTF8();
int row = model->countryModelRow(code);
countryCB_->setCurrentIndex(row);
},
[=] { // updateModelValue()
std::string code = model->countryCode(countryCB_->currentIndex());
model->setValue(UserFormModel::CountryField, code);
});
/*
* City
*/
auto cityCB = std::make_unique<Wt::WComboBox>();
cityCB->setModel(model->cityModel());
setFormWidget(UserFormModel::CityField, std::move(cityCB));
/*
* Birth Date
*/
auto dateEdit = std::make_unique<Wt::WDateEdit>();
auto dateEdit_ = dateEdit.get();
setFormWidget(UserFormModel::BirthField, std::move(dateEdit),
[=] { // updateViewValue()
Wt::WDate date = Wt::cpp17::any_cast<Wt::WDate>
(model->value(UserFormModel::BirthField));
dateEdit_->setDate(date);
},
[=] { // updateModelValue()
Wt::WDate date = dateEdit_->date();
model->setValue(UserFormModel::BirthField, date);
});
/*
* Children
*/
setFormWidget(UserFormModel::ChildrenField, std::make_unique<Wt::WSpinBox>());
/*
* Remarks
*/
auto remarksTA = std::make_unique<Wt::WTextArea>();
remarksTA->setColumns(40);
remarksTA->setRows(5);
setFormWidget(UserFormModel::RemarksField, std::move(remarksTA));
/*
* Title & Buttons
*/
Wt::WString title = Wt::WString("Create new user");
bindString("title", title);
auto button = bindWidget("submit-button", std::make_unique<Wt::WPushButton>("Save"));
bindString("submit-info", Wt::WString());
button->clicked().connect(this, &UserFormView::process);
updateView(model.get());
}
private:
void process() {
updateModel(model.get());
if (model->validate()) {
// Do something with the data in the model: show it.
bindString("submit-info",
Wt::WString("Saved user data for ")
+ model->userData(), Wt::TextFormat::Plain);
// Udate the view: Delete any validation message in the view, etc.
updateView(model.get());
// Set the focus on the first field in the form.
Wt::WLineEdit *viewField =
resolve<Wt::WLineEdit*>(UserFormModel::FirstNameField);
viewField->setFocus(true);
} else {
bindEmpty("submit-info"); // Delete the previous user data.
updateView(model.get());
}
}
std::shared_ptr<UserFormModel> model;
};
auto view = std::make_unique<UserFormView>();