Building a Contact Form with Customization

Last edit: 

Contributors: 

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

Requirements

Steps

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

  1. Define Contact – Customization
  2. Define Contact Form – Data Creation
  3. Fetch saved data with GraphQL
  4. Render Customizations within page
  5. Build Contact Form with HTML
  6. Delete Contact
  7. Edit Contact
  8. Add email notification
  9. Secure the form

Step 1: Define Contact – Custom Model Type

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

Create a file custom_model_types/contact.yml with the following content:

name: contact
properties:
- name: name
  type: string
- name: email
  type: string
- name: description
  type: string

The basic Configuration of the Customization 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 see 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.

form_configurations/contact_form.liquid

---
name: contact_form
resource: contact
resource_owner: anyone
fields:
  properties:
    name:
      validation:
        presence: true
    email:
      validation:
        presence: true
        email: true
    description:
      validation:
        presence: true
---

The above definition whitelists name, email, and description attributes and defines validation for each attribute. You can now use this form, and send requests to the Customization create endpoint


 curl -X POST https://example.com/api/customizations.json \
   -H "Authorization: Token token=[YOUR API TOKEN]" \
   -H "Accept: application/vnd.nearme.v4+json"\
   -H "Content-type: application/json"\
   -d \
   '
   {
     "form_configuration_name": "contact_form",
     "parent_resource_id": "contact",
     "customization": {
       "properties_attributes" : {
         "name": "User Name",
         "email": "user@example.com",
         "description": "Please contact me."
       }
     }
   }
   '
  • Please note that when you use curl you need to set proper headers. To obtain an API token, go to your site admin panel /instance_admin/settings/api_keys, and click on generate token.
  • From the example above you are sending the following parameters in the request:
    • form_configuration_name: name of the form object that you have created before
    • parent_resource_id: parameterized name of Custom Model Type, Contact becomes contact
    • customization: json attributes that correspond with structure of form and customization. Please note that Properties are nested within "properties_attributes" node.

Step 3: Fetch saved data with GraphQL

Now that you have created the Contact custom model type 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 graph_queries directory.

query get_contacts($id: ID) {
  customizations(name: "contact", id: $id) {
    results {
      id
      name: property(name: "name")
      email: property(name: "email")
      description: property(name: "description")
    }
  }
}

To see 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 markeptlace-kit gui

Step 4: Render Customizations within page

Display contacts as a list with the possiblity 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, see Pages documentation.


---
slug: contacts
---
{%- assign id = context.params.slug2 -%}
{%- query_graph "get_contacts", result_name: "g", id: id -%}

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

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>
</ul>

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 Customization 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: Customization
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_builder.fields.properties.name.name }}" value="{{ form_builder.fields.properties.name.value }}" id="name" type="text">
  {% if form_builder.errors['properties.name'] %}
    <p>{{ form_builder.errors['properties.name'] }}</p>
  {% endif %}

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

  <label for="description">Description</label>
  <textarea name="{{ form_builder.fields.properties.description.name }}" id="description">{{ form_builder.fields.properties.description.value }}</textarea>
  {% if form_builder.errors['properties.description'] %}
    <p>{{ form_builder.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 Configuration Documentation.

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


{% include_form 'contact_form' %}

Similarly to the curl request in step 2, you have to pass the Custom Model Type parameterized name as parent_resource_id, and point to the name of the form to include it properly in the page. As a result you should be able to create Customization objects, called Contact with simple form submission, and list them after fetching with GraphQL. You are not setting up headers and API keys with this approach, as those are included automagically with the form tag.

Step 6: Delete contact

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

---
name: delete_contact_form
resource: 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 "delete_contact_form", id: contact.id %}
</td>

Step 7: Edit Contact

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

---
slug: contacts/edit
---
{% include_form "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.

File should be located in the notifications/email_notifications, name it contact_notification.liquid:


---
name: contact_notification
delay: 0
enabled: true
from: 'Example Site <info@example.com>'
subject: Contact
to: '{{ form.properties.email }}'
trigger_condition: true
---
<h2>Hi {{ form.properties.name }}</h2>
<p>Thank you for your request, we will contact you shortly!</p>

As you can see, all request parameters are now accessible in form.model object, so you can simply compose any email you want. Next step is linking contact_notification in the contact_form by adding to form configuration inside form_configurations/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. You can also review example code commit for this particular article.

Next steps

Congratulations! You have learned how to build forms with Customization. You can now explore our reference documentation for Properties and Custom Model Types:

Questions?

We are always happy to help with any questions you may have. Check out our Help page, or contact us.