npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@apostrophecms/form

v1.4.2

Published

Build forms for ApostropheCMS in a simple user interface.

Downloads

1,066

Readme

Allow ApostropheCMS editors to build their own forms. They can then place any form in one or more content areas across the website.

Installation

npm install @apostrophecms/form

Use

Initialization

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.

Module configuration

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. |

formWidgets option

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': {}
    }
  }
};

select field widget option

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
  }
}

Supporting file field uploads safely

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.

⚠️ Public access allows anyone to upload files to your media storage. It is then recommended that any public form using a file field should also use the reCAPTCHA validation option or other means to prevent spam submissions.

Adding forms to pages and pieces

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': {}
          }
        }
      }
    }
  }
}

Handling submissions

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) |

Email

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.

Styling

Custom class prefix

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.

Using reCAPTCHA for user validation

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:

  1. Configure the secret key and site key as hard-coded options.
  2. 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.

In-code configuration example:

// modules/@apostrophecms/form/index.js
module.exports = {
  options: {
    recaptchaSecret: 'YOUR SECRET KEY',
    recaptchaSite: 'YOUR SITE KEY'
  }
}

Custom field validation

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:

  1. Assigning the original function to a variable.
  2. Creating a new function that uses the original one, adds functionality, and returns an identically structured response.
  3. 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 |