Allow ApostropheCMS editors to build their own forms. They can then place any form in one or more content areas across the website.
npm install @apostrophecms/form
Configure @apostrophecms/form
and the form widgets in app.js
. @apostrophecms/form
must appear before the form widget and form field widget modules. All the field widget modules below are included in this forms module bundle.
require('apostrophe')({
shortName: 'my-project',
modules: {
// The main form module
'@apostrophecms/form': {},
// The form widget module, allowing editors to add forms to content areas
'@apostrophecms/form-widget': {},
// Form field widgets, used by the main form module to build forms.
'@apostrophecms/form-text-field-widget': {},
'@apostrophecms/form-textarea-field-widget': {},
'@apostrophecms/form-select-field-widget': {},
'@apostrophecms/form-radio-field-widget': {},
'@apostrophecms/form-file-field-widget': {},
'@apostrophecms/form-checkboxes-field-widget': {},
'@apostrophecms/form-boolean-field-widget': {},
'@apostrophecms/form-conditional-widget': {},
'@apostrophecms/form-divider-widget': {},
'@apostrophecms/form-group-widget': {}
}
});
Why do we include all these modules?
- The first module added,
@apostrophecms/form
, is the piece type module for forms and includes most other related functionality. - The second,
@apostrophecms/form-widget
is the widget used to add forms to content areas. - The other widget modules are different types of form fields. We only have to include the field types we want to make available to editors.
Note: If you will be using the option to send email notifications you will need to configure the @apostrophecms/email
module as well. See the email notifications section.
There are a few options available for the Apostrophe form module.
Property | Type | Description |
---|---|---|
formWidgets |
Object | A widget configuration object for form widgets to use. |
saveSubmissions |
Boolean | Set to false to prevent Apostrophe from saving submissions in the aposFormSubmissions database collection. See submission information below. |
emailSubmissions |
Boolean | Set to false to disable the email notification fields on forms. See email information below. |
recaptchaSecret |
String | The "secret key" from a configured reCAPTCHA site. See reCAPTCHA details below. |
recaptchaSite |
String | The "site key" for a configured reCAPTCHA site. See reCAPTCHA details below. |
classPrefix |
String | A namespacing string used to build CSS classes on form elements for custom styling. |
The formWidgets
option allows us to change the widgets allowed in a form. It is configured exactly the same as any area's widget configuration. Most of these will likely be the form field widgets. The default configuration is:
{
'@apostrophecms/form-text-field': {},
'@apostrophecms/form-textarea-field': {},
'@apostrophecms/form-boolean-field': {},
'@apostrophecms/form-select-field': {},
'@apostrophecms/form-radio-field': {},
'@apostrophecms/form-checkboxes-field': {},
'@apostrophecms/form-conditional': {},
'@apostrophecms/form-divider': {},
'@apostrophecms/rich-text': {
toolbar: [
'styles', 'bold', 'italic', 'link',
'orderedList', 'bulletList'
]
}
}
This includes the rich text widget so editors can add directions or notes in the form. The file field widget is not included by default since site owners should carefully consider the implications of potentially public upload access. See the following section on file field support. The group widget is a simple fieldset container for other form field widgets and is not included by default either.
Any widget type can be included. A very simple form widget configuration might look like this:
// modules/@apostrophecms/form/index.js
module.exports = {
options: {
formWidgets: {
'@apostrophecms/form-text-field': {},
'@apostrophecms/form-textarea-field': {},
'@apostrophecms/form-boolean-field': {},
'@apostrophecms/form-radio-field': {},
'@apostrophecms/form-checkboxes-field': {},
'@apostrophecms/form-divider': {}
}
}
};
The select field widget has an option allowMultiple
to allow multiple select options to be selected. The default value is false
.
Once set to true
, it will add two new fields to the select field widget schema:
Property | Type | Description |
---|---|---|
allowMultiple |
Boolean | Set to true to enable multiple values to be selected in the select widget options, default value is false
|
size |
Integer | Number of options in the list that should be visible, default value is 0
|
// modules/@apostrophecms/form-select-field/index.js
modules.exports = {
options: {
allowMultiple: false
}
}
The file field widget, @apostrophecms/form-file-field-widget
, uses a route on the form widget that allows anonymous site visitor to include files (e.g., PDFs, images) in form submissions. Those uploads are stored in the same uploads directory as all other Apostrophe uploads, such as media library images, and they each get a document in the aposAttachments
database collection. The file access URL is included in the form submission data along with other submission data.
Forms are added to pages and pieces using a widget, @apostrophecms/form-widget
. Add this to an area in a page or piece type field schema to let editors add a form there. It will often be best to have an area field dedicated to the form widget.
// modules/contact-page/index.js
module.exports = {
options: {
label: 'Contact page'
},
fields: {
add: {
contactForm: {
type: 'area',
options: {
max: 1,
widgets: {
'@apostrophecms/form': {}
}
}
}
}
}
}
By default, submissions are saved to a new MongoDB collection, aposFormSubmissions
. If you do not want submissions saved to this collection, add the saveSubmissions: false
option to the @apostrophecms/form
module.
// modules/@apostrophecms/form/index.js
module.exports = {
options: {
saveSubmissions: false
}
}
Form submission always triggers a 'submission'
server-side event that you can listen for and handle in the @apostrophecms/form
module or another module. Event handler functions are passed the following arguments:
Argument | Description |
---|---|
req |
The request object from the submission |
form |
The form object |
submission |
The user submission |
In addition, if saving to the MongoDB collection is not disabled, form submission triggers a 'beforeSaveSubmission'
server-side event that you can listen for and handle in the @apostrophecms/form
module or another module. Event handler functions are passed the following arguments:
Argument | Description |
---|---|
req |
The request object from the submission |
info |
An object with form , data , and submission properties |
This provides an opportunity to modify info.submission
just before it is inserted into the database.
The module also emits browser events on the body
element on submission (@apostrophecms/form:submission-form
) and submission failure (@apostrophecms/form:submission-failed
). The browser events will include the following properties:
Argument | Description |
---|---|
form |
The form object |
formError |
The error object in case of a thrown error (null when successful) |
If @apostrophecms/email
is configured, submissions can be sent to multiple email addresses as well. In the "After-Submission" tab, enter a comma-separated list of email addresses to the "Email Address(es) for Results" field. If not using this feature, set the emailSubmissions: false
on the @apostrophecms/form
module to hide the related field on forms.
Need more control over your styling? You can include your own class prefix that will be included on most of the labels, inputs, and message/error elements within the forms. The class that is created uses the BEM convention. You add the prefix you want in the @apostrophecms/form
configuration.
'@apostrophecms/form': {
options: {
classPrefix: 'my-form'
}
}
This results in a class like my-form__input
being added to input elements in the form, for example.
Google's reCAPTCHA is built in as an optional feature. You will first need to set up reCAPTCHA on your website using the version three option. Make sure your domains are configured (using localhost
for local development).
Copy the site key and secret key. You will need to enter them in the site's global settings when logged in. Each form will have a checkbox to enable reCAPTCHA for that form.
You have two options for configuring reCAPTCHA:
- Configure the secret key and site key as hard-coded options.
- Allow site admins and editors to configure those values as global settings in the UI.
If you do not configure both the secret key and the site key in code, the global settings for the site will have fields for admins and editors to configure this themselves. This will be the case if you only configure one of the two in code. If you configure both in code, that global setting UI will not be available and the module will use the hard-coded values even if the global settings document has values for them already.
Once these values are configured, each form will have the option to enable reCAPTCHA independently.
// modules/@apostrophecms/form/index.js
module.exports = {
options: {
recaptchaSecret: 'YOUR SECRET KEY',
recaptchaSite: 'YOUR SITE KEY'
}
}
Each field returns its value from a collector function located on the apos.aposForm.collectors
array in the browser. You can extend these collector functions to adjust the value or do additional validation before the form posts to the server. You can write them as asynchronous functions if needed.
Collector functions take the widget element as an argument and return a response object on a successful submission. The response object properties are:
Property | Description |
---|---|
field |
The field element's name attribute (identical to the field widget's name property) |
value |
The field value |
These functions can be extended for project-level validation using the super pattern. This involves:
- Assigning the original function to a variable.
- Creating a new function that uses the original one, adds functionality, and returns an identically structured response.
- Assigning the new function to the original function property.
An example of the text area field in a project's code might look like this:
// modules/@apostrophecms/form-textarea-field-widget/ui/src/index.js
export default () => {
const TEXTAREA_WIDGET = '@apostrophecms/form-textarea-field';
// 1️⃣ Store the original collector function on `superCollector`.
const superCollector = apos.aposForm.collectors[TEXTAREA_WIDGET].collector;
// 2️⃣ Create a new collector function that accepts the same widget element
// parameter.
function newCollector (el) {
// Get the response from the original collector.
const response = superCollector(el);
if (response.value && response.value.split(' ').length < 10) {
// Throwing an object if there are fewer than ten words.
throw {
field: response.field,
message: 'Write at least 10 words'
};
} else {
// Returning the original response if everything is okay.
return response;
}
}
// 3️⃣ Assign our new collector to the original property.
apos.aposForm.collectors[TEXTAREA_WIDGET].collector = newCollector;
};
If you want to indicate an error on the field, throw
and object with the following values (as shown above):
Property | Description |
---|---|
field |
The field element's name attribute (identical to the field widget's name property) |
message |
A string to display on the field as an error message |