Setting up inquiry forms with Kuroco and Nuxt.js

This tutorial explains how to set up inquiry forms for a Nuxt.js project implemented with Kuroco.

This tutorial assumes that you have a ready-to-use Kuroco-implemented Nuxt.js project. See: Creating content list page with Kuroco and Nuxt.js for a full tutorial on how to build one.

About Kuroco forms

The form function in Kuroco allows the user to define data to support the creation of HTML forms, set automatic replies, and view form data submitted by users.

You can manage form settings via the [Campaign] -> [Form] module of the admin panel.

Image from Gyazo

For more information, refer to the following user guide documents:

Before your form can be used, you need to configure various settings and codes on the front-end. The sections below explain the steps to enable forms in your Kuroco and Nuxt.js project.

1. Defining your form data

To create a new form, click [Add] in the upper right corner of the form list screen.

Image from Gyazo

You will be redirected to a new form editor. See: Form editor - Basic settings for a detailed description of each field. For this tutorial, we will create a sample form with the following entries:

Image from Gyazo

FieldValue
TitleTest form
DescriptionEnter description here.
Enter description here.
Enter description here.
Thank you messageEnter thank-you message here.
Enter thank-you message here.
Enter thank-you message here.

When you are done, click [Add] at the bottom of the screen.

Image from Gyazo

You will be redirected back to the form list, which now contains your newly created form.

Image from Gyazo

Note: The number displayed in the "ID" column is the unique identifier of the inquiry form.

The ID value of each entry is automatically generated and varies depending on the environment. In the above example, the ID number is 25. However, for the steps below, remember to substitute this value with the ID shown on your screen.

2. Verifying the form settings

Verify the item settings

On the form list screen, click the title of your new form.

Image from Gyazo

You will be redirected to the form editor screen. From there, click the [Field settings] tab.

Image from Gyazo

This will take you to the form field settings screen.

Image from Gyazo

Here, you can set HTML definitions of the inquiry form items.

"name", "email", and "message" have been preset, since they are required fields in Kuroco. To add a custom field, you can define it as desired in the blank fields below "message".

Image from Gyazo

For this part of the tutorial, we will use only the default fields.

Verify the responses

Click the [Answers] tab to view a list of user-submitted responses to the form.

Image from Gyazo

All responses submitted via the HTML form are displayed here.
(In the above screenshot, there are no records displayed, since the HTML form has not been created yet and hence no data has been submitted.)
Details such as the response contents can be verified on this screen or in the [Report] tab to the right.

3. Setting up endpoints for the form

Next, create two endpoints as follows and enable the form function on the front-end:

  • For form definition retrieval: (GET) form
  • For form data submission: (POST) form

A. (GET) form endpoint setup

In the sidebar menu, select the target API from [API] and click [Add new endpoint] on the endpoint list screen.

Image (fetched from Gyazo)

Set up your endpoint as follows:

Image (fetched from Gyazo)

Field(s)Sub-fieldSetting
Path-form
Enabled/DisabledEnabled
ModelCategoryInquiry
ModelInquiryForm, v1
Operationdetails
Authorization-None

For simplicity, we will omit the authorization settings here.

Swagger UI verification

Then, verify if the Swagger UI can retrieve the form you have defined. On the endpoint list screen, click [Swagger UI].

Image (fetched from Gyazo)

You will be redirected to the Swagger UI screen. Select the endpoint you created earlier.

Image (fetched from Gyazo)

Click [Try it out]

Image (fetched from Gyazo)

Enter the ID of your new form (in this case, 25) in the "inquiry_id" field and click [Execute].

Image (fetched from Gyazo)

The form will be converted to JSON and displayed under "Responses".

Image (fetched from Gyazo)

B. (POST) form endpoint setup

You also need to create an endpoint for submitting data from HTML to the target form.

As before, select your target API in the sidebar menu and click [Add new endpoint] on the endpoint list screen.

Image (fetched from Gyazo)

Set up your endpoint as shown below.

Similarly, we omit the authorization settings here for simplicity.

Image (fetched from Gyazo)

Field(s)Sub-fieldSetting
Path-form
Enabled/DisabledEnabled
ModelCategoryInquiry
ModelInquiryMessage, v1
Operationsend
Authorization-None

When you are done, click [+Add] to save these settings.

We have now set up two endpoints:

  • For form definition retrieval: (GET) form
  • For form data submission: (POST) form

Next, let's use them to create and submit your form.

4. Additional front-end modifications

In your Nuxt installation directory, make a form directory under pages and create a file entitled index.vue.

pages
 - form
   - index.vue 

Insert the following code into pages/form/index.vue:

index.vue
<template>
  <div>
    <h1>Form page</h1>

    <form v-if="!submitted" ref="form">
      <div v-if="error" class="error">
        <p v-for="(err, idx) in error" :key="idx">
          {{ err }}
        </p>
      </div>

      <div class="row--status">
        <h2>Form title</h2>
        <div>{{ name }}</div>
      </div>

      <div class="row--status">
        <h2>Description</h2>
        <div>
          <p v-for="(line, idx) in textLines2texts(info)" :key="idx">
            {{ line }}
          </p>
        </div>
      </div>

      <div class="row--status">
        <h2>Thank you message</h2>
        <div>
          <p v-for="(line, idx) in textLines2texts(thanksText)" :key="idx">
            {{ line }}
          </p>
        </div>
      </div>

      <div class="row--status">
        <h2>Form fields</h2>
        <div class="row--internal">
          <div v-for="col in cols" :key="col.key">
            <p>[{{ col.title }}]</p>
            <pre>{{ col }}</pre>
          </div>
        </div>
      </div>

      <div v-for="col in cols" :key="col.objKey" class="row--form">
        <h2>[{{ col.title }}]</h2>
        <input :name="col.objKey" type="text" />
      </div>

      <div class="row--bottom-next">
        <button @click="handleOnSubmit">submit</button>
      </div>
    </form>

    <form v-else>
      <div class="row--status">
        <h2>Inquiry ID</h2>
        <div>
          {{ submittedId }}
        </div>
      </div>

      <div class="row--status">
        <h2>Thank you message</h2>
        <div>
          <p v-for="(line, idx) in textLines2texts(thanksText)" :key="idx">
            {{ line }}
          </p>
        </div>
      </div>

      <div class="row--bottom-back">
        <button @click="handleOnBack">back</button>
      </div>
    </form>
  </div>
</template>

<script>
const FORM_ID = 25 // ID of the form

export default {
  async asyncData({ $axios }) {
    const response = await $axios.$get(
      process.env.BASE_URL + `/rcms-api/2/form/${FORM_ID}`
    )
    return {
      name: response.details.inquiry_name,
      info: response.details.inquiry_info,
      thanksText: response.details.thanks_text,
      cols: Object.entries(response.details.cols).map(([k, v]) => ({
        objKey: k,
        ...v,
      })),
    }
  },
  data: () => {
    return {
      submitted: false,
      submittedId: null,
      error: null,
    }
  },
  methods: {
    textLines2texts(textLines = '') {
      return textLines.split('\r\n')
    },
    async handleOnSubmit(e) {
      e.preventDefault()

      // collect input elements
      const formInputElements = Array.from(this.$refs.form.elements).filter(
        (elm) => elm.tagName.toLowerCase() === 'input'
      )

      // transform key:value inputs to an object
      const body = formInputElements
        .map((elm) => ({ [elm.name]: elm.value }))
        .reduce((prev, cur) => ({ ...prev, ...cur }), {})

      try {
        // post data
        const { id } = await this.$axios.$post(
          process.env.BASE_URL + `/rcms-api/2/form?id=${FORM_ID}`,
          body
        )
        this.error = null
        this.submittedId = id
        this.submitted = true
      } catch (e) {
        this.error = [`${e}`, ...e.response.data.errors]
      }
    },
    handleOnBack(e) {
      e.preventDefault()
      this.submitted = false
    },
  },
}
</script>

<style scoped>
input {
  width: 100%;
  border: none;
}

.error {
  color: red;
}
.error > *:first-child {
  font-weight: bold;
}

.row--status {
  display: flex;
  border-top: 1px solid black;
}
.row--status > *:first-child {
  background-color: yellow;
  min-width: 15rem;
  max-width: 15rem;
  border-right: 1px solid black;
}

.row--form {
  display: flex;
  border-top: 1px solid black;
}
.row--form > *:first-child {
  background-color: aquamarine;
  min-width: 15rem;
  max-width: 15rem;
  border-right: 1px solid black;
}

.row--bottom-next {
  padding: 8px 16px;
  display: flex;
  justify-content: flex-end;
}
.row--bottom-back {
  padding: 8px 16px;
  display: flex;
  justify-content: flex-start;
}

.row--internal {
  display: flex;
}

form > *:nth-last-child(2) {
  border-bottom: 1px solid black;
}
</style>

For const FORM_ID = 25 above, substitute the ID value with your own.

For /rcms-api/2/form/${FORM_ID} and /rcms-api/2/form?id=${FORM_ID}, enter the path shown on your endpoint list screen.

Image (fetched from Gyazo)

Browser verification

Next, verify the above file in youe browser.
If your local server is down, run npm run dev to access http://localhost:3000/form. You should see the screen below:

Image (fetched from Gyazo)

In the table below, the yellow rows display data extracted from the retrieved form definition.

Data contentMethod of access
Form title(response).details.inquiry_name
Description(response).details.inquiry_info
Thank you message(response).details.thanks_text
Form fields(response).details.cols

As shown in the asyncData() source code, you can verify the form definition retrieval using the correspondence table above. Default form items other than [Category] are returned as objects.

You can use the [Network] tab in the Chrome developer console to verify more detailed network information sent to and from Kuroco.

Send the HTML form data

Now we can send data from the HTML form based on the retrieved data definition. The green rows represent the input fields of the HTML form. Enter the values shown below:

Image (fetched from Gyazo)

FieldInput value
[name]Jane Doe
[email]test@example.com
[message]Test message.

Clicking [submit] sends the input data and redirects you to the next screen.

Image (fetched from Gyazo)

As shown by the handleOnSubmit() source code, this operation sends [Form field] data to Kuroco.

Data verification in Kuroco

On the form list screen, click [Test form].

Image from Gyazo

Click the [Answers] tab to view the list of form responses received. You should see the entry you submitted earlier.

Image (fetched from Gyazo)

You can click the "No." link ([159] in this example) to view the response details, which contain data submitted via the HTML form.

Image (fetched from Gyazo)

5. Form validation

Lastly, let's test the form validation function.

Revisit the form page and enter an invalid value in the [email] field. Sample input:

Image (fetched from Gyazo)

FieldInput value
[name]Error Test
[email]mail
[message]Form validation error message.

After you click [submit], an error message will be displayed at the top of the screen.

Image (fetched from Gyazo)

The above procedure verifies Kuroco's server-side validation. When an error occurs, the error content and HTTP status code are returned in the response body. As seen in the source code, you need to implement error handling on the front-end as well.

To validate dynamic forms, you will need custom implementations based on the values obtained from Kuroco.

6. Custom fields

In addition to the default form fields, you can freely define custom fields following the steps below.

Add a custom field

On the form list screen, click [Test form].

Image from Gyazo

Click the [Item settings] tab to display the inquiry field settings screen.

Image (fetched from Gyazo)

A. Single-line text box

To set up a single-line text field, enter the values shown below in a blank custom entry:

Image (fetched from Gyazo)

FieldInput value
Titleitem
Required attributeOptional
Answer format / Input restrictionShort text (InputBox)
Options settings(Leave blank)

As shown in the table above, you can also define validation attributes in "Required attribute" and "Options settings" to return input restriction content based on the form field attributes.

When you are done, click [Update] near the bottom of the screen.

Image (fetched from Gyazo)

Go back to the form page and verify that the form definition obtained from Kuroco has been updated. You should see that item has been added to [Form fields].

Image (fetched from Gyazo)

Now we can input some data to verify that the new [item] field is actually sending information to Kuroco. Fill in the green boxes as follows:

Image (fetched from Gyazo)

FieldInput value
[item]Custom field
[name]Jane Doe
[email]test@example.com
[message]Custom field message.

Click [submit] to send the data. Then, click the [Answers] tab on the Kuroco form editor screen to display the list of responses. Verify that the new entry has been added.

Image (fetched from Gyazo)

As before, click the "No." link ([166] in this example) to view the response details. The newly added field "item" should now be included.

Image (fetched from Gyazo)

B. Date entry

This field type allows users to enter the date in YYYY/MM/DD format.

Setup

To set up a date input field, enter the values shown below in a blank custom entry:

Image (fetched from Gyazo)

FieldInput value
Titledate
Required attributeOptional
Answer format / Input restrictionDate format
Options settings(Leave blank)

For the verification step, please clear any other custom fields you have set up prior.

As shown in the table above, you can also define validation attributes in "Required attribute" and "Options settings" to return input restriction content based on the form field attributes.

When you are done, click [Update] near the bottom of the screen.

Image (fetched from Gyazo)

Go back to the form page and verify that the form definition obtained from Kuroco has been updated.

Image (fetched from Gyazo)

Now we can input some data to verify that the new [date] field is actually sending information to Kuroco. Fill in the green boxes as follows:

Image (fetched from Gyazo)

FieldInput value
[name]Jane Doe
[date]1900/01/01
[email]test@example.com
[message]Date field message.

Click [submit] to send the data. Then, click the [Answers] tab on the Kuroco form editor screen to view the new response and verify the submitted data.

Image (fetched from Gyazo)

Validation check

Dates can be validated in Kuroco. For example, let's set up a validation check for dates between 10 years ago and the present.

In the [Field settings] tab of the form editor, click [Settings] next to the custom "date" field.

Image (fetched from Gyazo)

In the settings dialog, enter -10years for the "Relative start offset" field under [Input type].

Image (fetched from Gyazo)

Refer to the reference guide List of available fields for the inquiry form: Date format for more information.

After you implement this validation check, entering a date over 10 years before the current date will result in an error. To test this, enter 1900/01/01 in the "date" field of the form page as before and click [submit].

FieldInput value
[name]Jane Doe
[date]1900/01/01
[email]test@example.com
[message]Date field message.

Verify the error response through the message displayed at the top of the screen (scroll to the top if it is beyond the display range).

Image (fetched from Gyazo)

Then, input a date within 10 years from the current date and click [submit]. For this tutorial, we will use 2022/04/01 as an example.

Image (fetched from Gyazo)

Verify that the data is sent successfully.

Image (fetched from Gyazo)

C. File upload

This field type allows you to upload and submit files in the inquiry form.

Setup

To set up a file upload field, enter the values shown below in a blank custom entry:

Image (fetched from Gyazo)

FieldInput value
Titlefile
Required attributeOptional
Answer format / Input restrictionFile
Options settings(Leave blank)

You can also define validation attributes in "Required attribute" and "Options settings" to return input restriction content based on the form field attributes.

When you are done, click [Update] near the bottom of the screen to save these settings.

Image (fetched from Gyazo)

Go back to the form page and verify that the form definition obtained from Kuroco has been updated.

Image (fetched from Gyazo)

Create file upload endpoint

The implementation process is as follows:

  1. Upload your file to Kuroco.
  2. Submit the ID of the uploaded file.

In order to do that, you need to first create an endpoint to receive uploads. Select your target API in the sidebar menu and click [Add new endpoint] on the endpoint list screen.

Image (fetched from Gyazo)

Enter the following settings in the endpoint configuration dialog:

Image (fetched from Gyazo)

Field(s)Sub-fieldSetting
Path-file
Enabled/DisabledEnabled
ModelCategoryFiles
ModelFiles, v1
Operationupload
Authorization-None

For simplicity, we will omit the authorization settings here.

Swagger UI verification

The next step is to verify if Swagger UI can retrieve the new form definition. On the endpoint list screen, click [Swagger UI].

Image (fetched from Gyazo)

On the Swagger UI screen, select the file upload endpoint you created earlier.

Image (fetched from Gyazo)

Click [Try it out].

Image (fetched from Gyazo)

In the input field labeled "file", select a sample file from your local drive and click [Execute]. For this tutorial, we will use the logo image kuroco.png.

Image (fetched from Gyazo)

The file is uploaded and stored in a temporary location indicated by the file_id path.

Image (fetched from Gyazo)

You can view and verify the uploaded file by accessing the URL admin panel host name + file_id in your browser.

Image (fetched from Gyazo)

Form modification and file submission

Next, update pages/form/index.vue.

Modify the input field corresponding to the file upload field to enable file inputs:

       <div v-for="col in cols" :key="col.objKey" class="row--form">
         <h2>[{{ col.title }}]</h2>
-        <input :name="col.objKey" type="text" />
+        <input v-if="col.title === 'file'" :name="col.objKey" type="file" @change="uploadFile" />
+        <input v-else :name="col.objKey" type="text" />
       </div>
 
       <div class="row--bottom-next">

Also, set up the form to send the file to the endpoint as soon as it is uploaded and save the file_id in the response:

   data: () => {
     return {
       submitted: false,
       submittedId: null,
       error: null,
+      file_id: null,
     }
   },
   methods: {
+    async uploadFile(e) {
+      const fm = new FormData();
+      fm.append('file', e.target.files[0]);
+
+      const { file_id } = await this.$axios.$post(
+        process.env.BASE_URL + `/rcms-api/2/file`,
+        fm,
+        {
+          headers: {
+            'Content-Type': 'multipart/form-data', // required to post file as a binary
+          },
+        }
+      );
+      this.file_id = file_id;
+    },
     textLines2texts(textLines = '') {

For the sent data, manually set the file_id to the corresponding data input of the file submission field:

         .map((elm) => ({ [elm.name]: elm.value }))
         .reduce((prev, cur) => ({ ...prev, ...cur }), {})
 
+      // apply file_id instead of the actual file input value
+      body.ext_01 = {
+        file_id: this.file_id,
+      };
+
       try {
         // post data

Now, upload the image on the front-end and verify that the new file is sent to Kuroco. On the form page, fill in the green boxes as follows:

Image (fetched from Gyazo)

FieldInput value
[name]Jane Doe
[file]kuroco.png
[email]test@example.com
[message]File upload message.

Click [submit] to send the data. Then, click the [Answers] tab on the Kuroco form editor screen to display the list of responses. Verify that the new entry has been added.

Image (fetched from Gyazo)

As before, click the "No." link ([176] in this example) to view the response details. The newly added field "file" should now be included.

Image (fetched from Gyazo)

7. Front-end validation of custom fields

So far, we have covered validation procedures in Kuroco, which is only the back-end side of things. To perform further validation on the front-end, you will need to set it up on your own. Below is an example of a simple front-end validation check when adding a custom date field.

Kuroco returns the validation content you defined when retrieving the form content in a GET request. As shown on the form page, the options attribute is retrieved in the GET response.

Image (fetched from Gyazo)

Set this value as min in the HTML input.

First, since the validator value is compatible with the PHP function strtotime(), we need to install a library for JavaScript operations. Run the following code in your terminal:

npm install locutus

Next, modify pages/form/index.vue to import the library into the component (i.e., make it available):

 </template>
 
 <script>
+import strtotime from 'locutus/php/datetime/strtotime';
+
 const FORM_ID = 21 // Substitute with your own form ID.
 
 export default {

Implement methods for strtotime() and the dependent parser:

   },
   methods: {
+    strtotime,
+    getMin(startPeriodStr) {
+        const minDateNum = strtotime(startPeriodStr) * 1000; // to millisec
+        return new Date(minDateNum).toJSON().split('T')[0] // to YYYY-MM-DD
+    },
     textLines2texts(textLines = '') {
       return textLines.split('\r\n')
     },

Finally, update the contents of the HTML template:

       <div v-for="col in cols" :key="col.objKey" class="row--form">
         <h2>[{{ col.title }}]</h2>
-        <input :name="col.objKey" type="text" />
+        <input v-if="col.title === 'date'" type="date" :min="getMin(col.options.minPeriod)" />
+        <input v-else :name="col.objKey" type="text" />
       </div>
 
       <div class="row--bottom-next">

After the implementation is complete, revisit the form page and verify that values prior to the specified date can no longer be selected.

Image (fetched from Gyazo)

If you have any other questions, please use our contact form or Slack workspace.