Forms in CppCMS
Table of Contents
- Base Concepts
- Class
form
- Class
widgetset
- Widgets
- Dynamic Forms Creation
- Extending Unsupported Features
Basic Concepts
First of all, it is recommended to read a tutorial Start With Forms
Forms system of CppCMS has three major types of classes:
- Form --
cppcms::form
--- the base class for form container, It is responsible on form validation, rendering. Usually, user derives its own classes fromcppcms::form
- Widgets in the namespace
cppcms::widgets
. They are basic elements of form like input text, number, checkbox, submit button etc. cppcms::widgetset
--- simple class for partial rendering of forms for easier integration with templates.
All of them are derived from base_form
that provide basic
interface for loading, rendering and validation of forms.
Class cppcms::form
This class used as base class for any HTML form developer wants to create. This class is non-copyable and used as container for loading, validating and rendering widgets.
Member functions
void append(base_form *)
register widget to the form.When form is created every widget should be registered for validation and rendering. It can be done by calling
append()
function.form &operator &(base_form &f)
-- syntactic sugar forappend()
. For example:struct sum : public cppcms::form { widgets::number<int> M,N; widgets::submit button; my_form() : M("m","M"), N("n","N"), button("submit","Sum") { *this & M & N & button; } };
Now, when the form is rendered it is renders all widgets given in this list.
virtual void load(cgicc::Cgicc const &cgi)
-- load form data for CGI environment. It is used when we need to load submitted data from user. For example:if(env->getRequestMethod()=="POST") { sum.load(*cgi);
virtual bool validate()
-- validate loaded data. This method is always called after load. It is used for checking validity of all form widgets.if(env->getRequestMethod()=="POST") { sum.load(*cgi); if(!sum.validate()) { // Process request } } // Continue with empty or partially filled form
This method is usually overridden for forms developer in order to implement advanced tests that are not available on widgets level. For example:
struct sum : public form { widgets::number<int> odd,even; ... virtual bool validate() { if(!form::validate()) return false; if(odd.get() % 2 == 1) return true; odd.not_valid(); return false; } };
When you re-implement validation, you should always perform base form validation and then perform your own tests.
Generally, best practice is performing all possible tests on form level, even complex things like keys validity and so on, in order make use of forms transparent for developer, i.e.: if form is valid then it is 100% valid.
void clear()
--- reset the form, clear all widgets.
Class cppcms::widgetset
Is a container that is similar to form
but it is used only
for rendering different types of widgets.
It has following important member function:
widgetset &operator<<(widgets::base_widget &w);
That allows registering of various widgets to the set. For example:
struct op { widgets::number<int> x,y; widgets::button add,sub,mul,div; widgetset inputs; widgetset ops; op() : ... { *this & x & y & add & sub & mul & div; inputs<<x<<y; ops<<add<<sub<<mul<<div; } };
Now can write template:
<table> <% form as_table ops_form.inputs %> </table> <p><% form as_space ops_form.ops %></p>
Now push buttons and input fields are rendered using different methods.
Widgets
Widgets are used for actual data loading and rendering. Each widget is derived from widgets::base_widget
that hold all
common data for all widgets. It is also used for creation
of custom widgets by users.
Core Widget base_widget
Public Members
bool is_set
-- used to determinate whether this widget has assigned content. Usually it is set totrue
after loading form withform::load()
. It is also set to true during manual assignment of value.string id
-- HTML "id" ofinput
tag. If it is empty, no HTML "id" is rendered. Generally used when user side validation is required.string name
-- the "name" filed of HTMLinput
tag. It is used for identifying data in forms. This member must be not-empty for correct form loading and rendering.string msg
-- the message that describes the widget like "Username", "Password". Usually it should be provided.string error_msg
-- the message that should be displayed for this widget if its input is invalid. Iferror_msg
is empty little start "*" is displayed.string help
-- additional help string that can be displayed for the widget, usually it is expanded description ofmsg
.bool is_valid
-- the flag that is used to check whether loaded widget has valid information.
Note: Generally you should not assign values of is_set
and is_valid
directly. They are provided as
public members for simpler integration with template
system for custom widgets rendering.
Public Member Functions
void not_valid()
--- invalidate widget content. It is generally called after additional checks that are performed on form level.
Other functions would be described in section of custom widgets creation.
Text Input: widgets::text
This is text input widget.
text(string name,string msg)
-- creates text input widget with name and short description.set_limits(int min,int max)
-- set limits on field size, where max=-1 --- no limit.set_nonempty()
-- same asset_limits(1,-1)
. This field should not be empty.set_limits(string error_message,int min,int max)
-- Sets limits and a message that should be displayed in case of error.void set(string const &)
-- set the value to this field.string const &get()
-- get the values of this field.string &str()
-- set value by reference. Is useful for reducing memory copying for big strings.string text=get_text_from_db(); form.content.str().swap(text);
string value
-- the content of the widget, you should never set its value directly! It may be used only for special form rendering, for example:<input type="text" name="<% frm.msg.name %>" value="<% frm.msg.value %>" onclick="alert('Clicked!')" />
Which can't be rendered using normal
<% form as_p frm %>
.For any other purposes, use
set()
,get()
andstr()
member functions.
Numerical Input: widgets::number<>
This widget is defined as:
template<typename N> number : public text { ... };
It is used as input for numerical values.
In addition to text
members it implements following member functions:
number(string name,string msg)
-- constructor. It has same parameters astext
.void set_low(N a)
-- set minimal allowed value for the number.void set_high(N a)
-- set maximal allowed value;void set_range(N a,N b)
--- same asset_low(a)
,set_high(b)
.N get() const
--- returns widget valuevoid set(N v)
--- set initial value
Note: This widget uses boost::lexical_cast
for conversion. Any type that is supported
by boost::lexical_cast
is supported by number
widget.
Password Input: widgets::password
Used for input password values. It inherits from widgets::text
.
password(string name,string msg)
-- set name and message for password, similar for text widget.void set_equal(password &p2)
-- adds additional validity check --- password confirmation.Note: It stores a pointer to another password widget. So make sure, that other widget does not moved or get deleted.
Text Area: widgets::textarea
Inherits from widgets::text
.
textarea(string name,string msg)
-- same constructor as ofwidgets::text
.int rows,cols
-- number of rows and columns of text area. Default is -1 --- undefined. You should set these values directly.
Complex Field Validation: widgets::regex_field
This widget allows you to check an input using regular expression. It uses boost::regex
for this purpose.
It inherits from widgets::text
as well.
regex_field(boost::regex const &e,string name,string msg)
-- create field using regular expression.
Note: You should provide regular expression of your own. For example:
static const regex date("\\d\\d-\\d\\d-\\d\\d\\d\\d"); regex_field date_input(date,"data","Enter Date");
Hidden Field: widgets::hidden
Hidden field can be used as widgets::hidden
. This class inherits for widgets::text
and by default it is set to non-empty.
Check Box: widgets::checkbox
This widget renders a checkbox.
Constructor:
checkbox(string name,string msg) -- construct checkbox using name. Default value for checkbox
value
field is "1". Thus usually you distinguish several checkboxes using different names. For example:checkbox has_car("car","Have Car"); checkbox married("married","Married");
Would cause following "GET" method url:
/?car=1&married=1
Members Functions:
void set(string const &value)
-- set the value for checkbox. For example:vector<checkbox> options; for(i=0;i<choises.size();i++) { options.push_back(checkbox("opt",i)); }
That may generate following url: '/?opt=1&opt=2&opt=1'
void set(bool)
-- set checked state.bool get()
-- return whether this checkbox was checked or not.
Public Members For Form Rendering
Note: You should never set these members directly. You may use them only for form rendering:
string input_value
--value
part of input HTML element.bool value
-- true if this checkbox should be "checked".
For example:
<intput name="<% form.opt.name %>" value="<% form.opt.input_value %>" <% if form.opt.value %>checked="checked"<% end %> onclick="alert('Clicked!')" />
Single Choice Select
There are two widgets that are used for these purpose: select
and radio
that both
inherit from select_base
.
select_base
members
void add(string value,string option)
-- add a select option namedoption
with valuevalue
.void add(string value)
-- same asadd(value,value)
. Note, generally, the first is actual name used for internal option representation, the second one is how it is displayed --- option name is usually localized. So usually you call:opts.add("apple",gettext("Apple"));
void add(int value,string option)
-- add a select option with numerical representation.void add(int value)
--- same asadd(value,tostring(value))
.
Usually you should use string values or integer values and not mix them.
void set(string value)
-- set selected value.void set(int value)
-- set selected integer value.string get()
-- get selected value. Note: if you usedadd(int,string)
you would get this option converted to string.int geti()
-get integer value. If the option is textual, result is undefined.
Public members for rendering:
struct option { string value,option; };
-- internal representation of values.list<option> select_list
-- list of available options.string value
-- the selected value of option.
Both widgets::select
and widgets::radio
has following ordinary constructors:
select(string name,string msg)
radio(string name,string msg)
widgets::select
members
void set_size(int n)
-- set number of lines for input drop-down list.
widgets::radio
members
void set_vertical()
-- set vertical rendering of radio buttons list.
Multiple Selection widgets::select_multiple
Multiple selection drop down list:
Constructor
select_multiple(string name,int rows,string msg="")
-- creates multiple select widget that hasrows
input rows.
Public Member Functions
void add(string val,string opt,bool selected=false)
-- add selection option with valueval
and descriptionopt
.void add(int val,string opt,bool selected=false)
-- same as above but use integer value.void add(string v,bool s=false)
-- same asadd(v,v,s)
void add(int v,bool s=false)
-- same asadd(v,tostring(v),s)
These functions are used similarly to those of select_base
but they have selection marks options. Generally, you
should not use both add(int,...)
and add(string,...)
.
set<string> &get()
-- returns set of selected options.set<int> geti()
-- returns set of selected integer options.set_min(int n)
-- set minimal number of options that should be selected.
Public Members for Form Rendering
int size
-- number of rows in select listset<string> chosen
-- selected options.map<string,string> available
-- set of all available options (first) and their names (second)
As in above cases these members may be used only for form rendering. Do not change them directly.
Submit Button: widgets::submit
submit(string name,string button_value,string msg="")
-- Creates button with textbutton_value
.bool pressed
--- this flag is true if this button was pressed.string value
--- value member that should be used only for rendering.
Note: This widget checks only name
filed and ignores value that may be localized. Thus, you
should generally specify different names for different buttons:
remove("rem",gettext("Remove")), save("save",gettext("Save"))
Dynamic Creation Of Forms
In many cases it is required to create forms on-the-fly according to data that should be used. This can be done simply --- all widgets are default and copy constructable and assignable --- thus you just can create STL collections of them.
However, you should remember, that when you register your widgets in cppcms::form
or in cppcms::widgetset
you pass pointers to them and not their copies. Thus, be careful, make sure your widgets are
not copied to other place.
For example:
struct opts : public form { std::vector<widgets::checkbox> options; widgets::submit button; opts(); }
Wrong Code:
opts::opts() : button("send","Send") { for(i=0;i<10;i++) { options.push_back(widgets::checkbox("option_"+lexical_cast<string>(i), "Chose "_lexical_cast<string>(i+1))); *this & options.back(); } *this & button; }
Correct Code:
opts::opts() : button("send","Send") { options.resize(10); for(i=0;i<10;i++) { options[i]=widgets::checkbox("option_"+lexical_cast<string>(i), "Chose "_lexical_cast<string>(i+1))); *this & options[i]; // Now vector would not reallocate its members } *this & button; }
Extending and Unsupported Features
If any of given widgets does not satisfy your needs you may:
- Create your own widgets. See,
form.h
andform.cpp
for examples. - Use
cgicc
library directly without no restriction.