Form validation

Learn how to add form validation from front to back-end

Form validation

Now that we have a contact form and a package for our actions, we have to handle the submission. We can do that by adding a new action that will be called when the form is submitted. Let’s create a submit.js file in our packages/contact folder.

function main(args) {
  let message = []
  let errors = []
  // TODO: Form Validation
  // TODO: Returning the Result
}

This action is a bit more complex. It takes the input object (called args) which will contain the form data (accessible via args.name, args.email, etc.). With that. we will do some validation and then return the result.

Validation

Let’s start filling out the “Form Validation” part by checking the name:

// validate the name
if(args.name) {
  message.push("name: "+args.name)
} else {
  errors.push("No name provided")
}

Then the email by using a regular expression:

// validate the email
var re = /\S+@\S+\.\S+/;
if(args.email && re.test(args.email)) {
    message.push("email: "+args.email)
} else {
  errors.push("Email missing or incorrect.")
}

The phone, by checking that it’s at least 10 digits:

// validate the phone
if(args.phone && args.phone.match(/\d/g).length >= 10) {
  message.push("phone: "+args.phone)
} else {
  errors.push("Phone number missing or incorrect.")
}

Finally, the message text, if present:

// validate the message
if(args.message) {
  message.push("message:" +args.message)
}

Submission

With the validation phase, we added to the “errors” array all the errors we found, and to the “message” array all the data we want to show to the user. So if there are errors, we have to show them, otherwise, we store the message and return a “thank you” page.

// return the result
if(errors.length) {
  var errs = "<ul><li>"+errors.join("</li><li>")+"</li></ul>"
  return {
    body: "<h1>Errors!</h1>"+
      errs + '<br><a href="javascript:window.history.back()">Back</a>'
    }
} else {
    var data = "<pre>"+message.join("\n")+"</pre>"
    return {
      body: "<h1>Thank you!</h1>"+ data,
      name: args.name,
      email: args.email,
      phone: args.phone,
      message: args.message
    }
}

Note how this action is returning HTML code. Actions can return a { body: <html> } kind of response and have their own url so they can be invoked via a browser and display some content.

The HTML code to display is always returned in the body field, but we can also return other stuff. In this case we added a a field for each of the form fields. This gives us the possibility to invoke in a sequence another action that can act just on those fields to store the data in the database.

Let’s start deploying the action:

ops action create contact/submit submit.js --web true
ok: created action contact/submit

The --web true specifies it is a web action. We are creating a submit action in the contact package, that’s why we are passing contact/submit.

You can retrieve the url with:

ops url contact/submit

$ <apihost>/api/v1/web/openserverless/contact/submit

If you click on it you will see the Error page with a list of errors, that’s because we just invoked the submit logic for the contact form directly, without passing in any args. This is meant to be used via the contact form page!

We need to wire it into the index.html. So let’s open it again and add a couple of attributes to the form:

---             <form method="POST"> <-- old
+++            <form method="POST" action="/api/v1/web/openserverless/contact/submit"
                enctype="application/x-www-form-urlencoded"> <-- new

Upload the web folder again with the new changes:

ops web upload web/

Now if you go to the contact form page the send button should work. It will invoke the submit action which in turn will return some html.

If you fill it correctly, you should see the “Thank you” page.

Submit Result

Note how only the HTML from the body field is displayed, the other fields are ignored in this case.

The ops action command can be used for many more things besides creating actions. For example, you can use it to list all available actions:

ops action list

actions
/openserverless/contact/submit               private nodejs:18

And you can also get info on a specific action:

ops action get contact/submit

{
    "namespace": "openserverless/contact",
    "name": "submit",
    "version": "0.0.1",
    "exec": {
        "kind": "nodejs:18",
        "binary": false
    },
  ...
}

These commands can come in handy when you need to debug your actions.

Here is the complete the submit.js action:

function main(args) {
  let message = []
  let errors = []

  // validate the name
  if (args.name) {
    message.push("name: " + args.name)
  } else {
    errors.push("No name provided")
  }

  // validate the email
  var re = /\S+@\S+\.\S+/;
  if (args.email && re.test(args.email)) {
    message.push("email: " + args.email)
  } else {
    errors.push("Email missing or incorrect.")
  }

  // validate the phone
  if (args.phone && args.phone.match(/\d/g).length >= 10) {
    message.push("phone: " + args.phone)
  } else {
    errors.push("Phone number missing or incorrect.")
  }

  // validate the message
  if (args.message) {
    message.push("message:" + args.message)
  }

  // return the result
  if (errors.length) {
    var errs = "<ul><li>" + errors.join("</li><li>") + "</li></ul>"
    return {
      body: "<h1>Errors!</h1>" +
        errs + '<br><a href="javascript:window.history.back()">Back</a>'
    }
  } else {
    var data = "<pre>" + message.join("\n") + "</pre>"
    return {
      body: "<h1>Thank you!</h1>" + data,
      name: args.name,
      email: args.email,
      phone: args.phone,
      message: args.message
    }
  }
}