Homepage

Building a Contact Form with Records

Last edit: Nov 03, 2023

This guide will help you understand the process of building a contact form backed up with Record. Whenever you need to build a simple contact form, blog functionality, or complex API endpoint, you can define any data structure you need with Records.

Requirements

Steps

Building a Contact Form with Records is a nine-step process:

Step 1: Define Contact – Table

To define a new Table, use YAML files placed in the schema directory. In the key-value format, you can define a name for your Record, and an array of Properties for the Record object. To build a Contact Form, you'll need email, name, and description attributes on the Record that you can call a contact.

app/schema/contact.yml
name: contact
properties:
- name: name
  type: string
- name: email
  type: string
- name: description
  type: string

The basic Configuration of the Record consists of a name and the list of Properties. In the example above, you have defined three attributes of type string.

For more information on Properties please visit the Properties Documentation.

Step 2: Define Contact Form – Form Object

Now that you have the definition of a Contact in place, you can create a Form that will configure the server to accept requests with data in the desired format. Data passed through the form will be stored in the database as a Contact object.

app/forms/contact_form.liquid
---
name: contact_form
resource: modules/contacts/contact
resource_owner: anyone
fields:
  properties:
    name:
      validation:
        presence: true
    email:
      validation:
        presence: true
        email: true
    description:
      validation:
        presence: true
---

The above definition allows name, email, and description attributes and defines validation for each attribute.

Step 3: Fetch saved data with GraphQL

Now that you have created the Contact Table in the previous step, you can proceed to fetch this data with GraphQL. To define get_contacts query, create file get_contacts.graphql file in graphql directory.

query get_contacts($id: ID) {
  records(
    filter: {
      table: { value: "modules/contacts/contact" },
      id: { value: $id }
    },
    per_page: 100
  ) {
    results {
      id
      name: property(name: "name")
      email: property(name: "email")
      description: property(name: "description")
    }
  }
}

To check if the above query works properly you can head to the next section to learn how to embed query results with page view or use pos-cli gui

Step 4: Render Records within page

Display contacts as a list with the possibility to get to the contact details view.
As a first step, you need a page that will respond to given paths:

  • GET /contacts for list view
  • GET /contacts/[ID] for detail view

Page slug for the above URL map will simply be contacts as the ID passed in the URL is dynamically assigned to the context.params.slug2 variable. For more information, visit the Pages documentation.

app/views/pages/contacts.liquid

{% liquid
  assign id = context.params.slug2
  graphql g = "modules/contacts/get_contacts", id: id
%}

{%- if id == blank -%}
  {% include 'modules/contacts/contacts/list', contacts: g.records.results %}
{%- else -%}
  {% include 'modules/contacts/contacts/show', contact: g.records.results.first %}
{%- endif -%}

app/views/partials/contacts/list.liquid

<h1>List View</h1>
<table>
  <tr>
    <th>ID</th>
    <th>Email</th>
    <th></th>
  </tr>
  {% for contact in contacts %}
  <tr>
    <td>{{ contact.id }}</td>
    <td>{{ contact.email }}</td>
    <td><a href="/contacts/{{ contact.id }}">Details</a></td>
  </tr>
  {% endfor %}
</table>

app/views/partials/contacts/show.liquid

<h1>Detail view</h1>
<p>{{ contact.name }}</p>
<p>{{ contact.email }}</p>
<p>{{ contact.description }}</p>

Step 5: Build Contact Form with HTML

In Step 2: Define Contact Form-Data Creation, you have created a form file called contact_form, that configures the form object. This was a minimal example to allow sending requests to our API endpoint, and create Record objects. Now you can extend this file with HTML, that renders the form to our users. The HTML part of the file comes after the configuration part, i.e. after the closing --- tag.

---
name: contact_form
resource: modules/contacts/contact
resource_owner: anyone
redirect_to: /contacts
fields:
  properties:
    name:
      validation:
        presence: true
    email:
      validation:
        presence: true
        email: true
    description:
      validation:
        presence: true
---

{% form %}
  <label for="name">Name</label>
  <input name="{{ form.fields.properties.name.name }}" value="{{ form.fields.properties.name.value }}" id="name" type="text">
  {% if form.errors['properties.name'] %}
    <p>{{ form.errors['properties.name'] }}</p>
  {% endif %}

  <label for="email">Email</label>
  <input name="{{ form.fields.properties.email.name }}" value="{{ form.fields.properties.email.value }}" id="email" type="email">
  {% if form.errors['properties.email'] %}
    <p>{{ form.errors['properties.email'] }}</p>
  {% endif %}

  <label for="description">Description</label>
  <textarea name="{{ form.fields.properties.description.name }}" id="description">{{ form.fields.properties.description.value }}</textarea>
  {% if form.errors['properties.description'] %}
    <p>{{ form.errors['properties.description'] }}</p>
  {% endif %}

  <button>Submit</button>
{% endform %}

The redirect_to configuration option defines where to redirect the user after successful form submission. The predefined form tag allows you to create context for submitted data.

For more information of advanced form configuration please visit Form Documentation.

To display the form on the previously created contacts page you have to include it with:

app/views/pages/contacts.liquid

{% include_form 'modules/contacts/contact_form' %}

As a result, you should be able to create Record objects, called Contact with simple form submission, and list them after fetching with GraphQL.

Step 6: Delete contact

In order to remove record from the database, create delete_contact_form:

---
name: delete_contact_form
resource: modules/contacts/contact
resource_owner: anyone
redirect_to: /contacts
fields: {}
---

{% form method: "delete" %}
  <button type="submit">Delete</button>
{% endform %}


To display delete button on the list of contacts, include the form as follows:

<td>
  {% include_form "modules/contacts/delete_contact_form", id: contact.id %}
</td>

Step 7: Edit Contact

As you already have the form to create contacts, you can reuse it for the purpose of record update. In order to prefill the form with proper data, add id to include_form tag.

app/views/pages/contacts/edit.liquid

{% include_form "modules/contacts/contact_edit_form", id: context.params.slug3 %}

Step 8: Add Email Notification

To send an Email Notification to the email that was sent with form submission, create an Email Notification file that will define email configuration and message template.

The file should be located in the emails directory, name it contact_notification.liquid:

app/emails/contact_notification.liquid

---
name: contact_notification
from: 'Example Site <[email protected]>'
subject: Contact
to: '{{ form.properties.email }}'
---
<h2>Hi {{ form.properties.name }}</h2>
<p>Thank you for your request, we will contact you shortly!</p>

All request parameters are now accessible in form.record object, so you can compose any email you want. Next step is linking contact_notification in the contact_form by adding to form configuration inside forms/contact_form.liquid file.

email_notifications:
- contact_notification

Step 9: Secure the form

At this point, you might want to restrict access to the forms and views you have created. You can do that using Authorization Policies.

Live example and source code

To play with a live example (in much fuller form and with additional information) go to the contact example page.

Source code can be found on GitHub.

Next steps

Congratulations! You have learned how to build forms with Record. You can now explore our reference documentation for Properties and Tables:

Questions?

We are always happy to help with any questions you may have.

contact us