This tutorial walks through how to create a sign up form with validation. 

Here is a sample, which you can copy into your own account: https://app.atomic.io/d/0FcjpwdPbt6l

There is a decent amount of scripting that is required for this prototype to work, however if you have never coded before we’ve made sure it is straightforward to use.

This tutorial covers the use case of a sign up or registration form, however the principles can be applied to any type of form you are prototyping.
 

Step 1: Generate your assets

Draw your elements on the canvas, or import them in from Sketch or Figma, and create an additional confirmation page. This new confirmation page will be where the user transitions to once they successfully complete the form. 

Rename your pages :

  • From Page 1 to Form, and 
  • From Page 2 to Loading 

Note: if you would like to include a loading page — like we have in our sample — then include a third page, and name the third page something like Result.
 

Step 2: Copy the sample to your project 

Next, if you haven't already, copy the sample prototype to your project:
https://app.atomic.io/d/0FcjpwdPbt6l

Open the share link above and then select ‘Copy & Edit”. This copies all of the components, and variables we need for this prototype.
 

Step 3: Add text elements, radio buttons, and checkboxes to your prototype

Now add these new components you’ve just imported to your prototype. 

In the editor, open the component browser by pressing the keyboard shortcut ‘C’ or select the ‘Place a component’ tool from the left hand toolbar. 

Select the component ‘textInputComponent’. 

Now duplicate the component instance for each item you require. For example, in our sample, we have two instances of this component: for ‘name’ and ‘email’. 

Now repeat this process, to place the other components on the canvas:

  • passwordInputComponent
  • termsCheckbox
  • radioButtons

Finally, repeat this process for the errorMessage component, placing an instance of it underneath each text input, radio button, and checkbox.
 

Step 4: Rename component instances

As the script we run looks for instances with specific names, we need to make sure our component instances are named correctly. 

Use these exact names for the text input and error state components:

  • Name: nameInput  & nameError 
  • Email: emailInput  & emailError 
  • Password: passwordInput   & passwordError 
  • Updates (radio buttons): udpatesRadioButtons  & updatesError 
  • Terms (checkbox): termsCheckbox  & termsError 

Tip: grouping components together with any associated elements (like labels) will make working with them in the Layers panel easier. 

Tip: the tabbing order in Preview will follow the reverse order of the fields as they appear in the Layers panel (from bottom to top), and so order the layers in the reverse order as you would like a user to tab through.
 

Step 5: Add the form validation script

We’re now going to add a script that contains all of the logic that controls the error states. 

When you copied the sample into the project, five project variables were also copied across. We’ll be referencing in the scripts. To see these variables, hit keyboard shortcut “V”, or select ‘manage project variables’ on the left of your screen. 

The first 9 lines of code we’re about to add all declare a variable to store the name of elements that we reference many times in the scripts. 

We then have functions for the various checks we want to make before the form can be submitted. All of these functions that are defined here can be called elsewhere within the prototype. For example, 

checkName checks there is text present;

checkEmail checks the validity of the email address; and

checkPassword validates the password

It’s now time to add the code to our prototype. On the page that contains the text inputs, add a Timer page action to wait 0ms , with an action of Script Expression, then add the following script:

const FORM_PAGE = "Form";

const NAME_INPUT = "nameInput";

const NAME_ERROR = "nameError";

const EMAIL_INPUT = "emailInput";

const EMAIL_ERROR = "emailError";

const PASSWORD_INPUT = "passwordInput";

const PASSWORD_ERROR = "passwordError";

const UPDATES_ERROR = "updatesError";

const TERMS_ERROR = "termsError";

// defines a function for testing if an email address is valid or not

validateEmail = function(email) {

  var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;

  return re.test(email);

}

checkName = function() {

  console.log("checkName");

  const pathToInstance = [FORM_PAGE, NAME_INPUT];

  const pathToInput = [FORM_PAGE, NAME_INPUT, "default", "input"];

  const userName = text(pathToInput);

  console.log("userName:", userName);

  // check if a name has been entered

  if(userName !== undefined && userName !== "") {

    state(pathToInstance, "default");

    state([FORM_PAGE, NAME_ERROR], "hide");

    return true;

  } else {

    state(pathToInstance, "error");

    state([FORM_PAGE, NAME_ERROR], "show");

    text([FORM_PAGE, NAME_ERROR, "show", "text"], "Please provide your name");

    return false;

  }

}

checkEmail = function() {

  console.log("checkEmail");

  const pathToInstance = [FORM_PAGE, EMAIL_INPUT];

  const pathToInput = [FORM_PAGE, EMAIL_INPUT, "default", "input"];

  const userEmail = text(pathToInput);

  const emailIsValid = validateEmail(userEmail);

  function setErrorMessage(message) {

    state(pathToInstance, "error");

    state([FORM_PAGE, EMAIL_ERROR], "show");

    text([FORM_PAGE, EMAIL_ERROR, "show", "text"], message);

  }

  // check if any email has been entered

  if(userEmail !== undefined && userEmail !== "") {

    // check if email address is valid

    if(emailIsValid) {

      state(pathToInstance, "default");

      state([FORM_PAGE, EMAIL_ERROR], "hide");

      return true;

    } else {

      setErrorMessage("Please provide a valid email address");

      return false;

    }

  } else {

    setErrorMessage("Please provide your email address");

    return false;

  }

}

checkPassword = function() {

  console.log("checkPassword");

  const pathToInstance = [FORM_PAGE, PASSWORD_INPUT];

  const pathToInput = [FORM_PAGE, PASSWORD_INPUT, "default", "input"];

  const userPassword = text(pathToInput);

  function setErrorMessage(message) {

    state(pathToInstance, "error");

    state([FORM_PAGE, PASSWORD_ERROR], "show");

    text([FORM_PAGE, PASSWORD_ERROR, "show", "text"], message);

  }

  // check if any password has been entered

  if(userPassword !== undefined && userPassword !== "") {

    // check if password is valid

    if(userPassword.length >= 8) {

      state(pathToInstance, "default");

      state([FORM_PAGE, PASSWORD_ERROR], "hide");

      return true;

    } else {

      setErrorMessage("Password must be at least 8 characters long");

      return false;

    }

  } else {

    setErrorMessage("Please provide a password");

    return false;

  }

}

checkUpdates = function($p) {

  console.log("checkUpdates", $p.userReceiveUpdates);

  const pathToInstance = [FORM_PAGE, UPDATES_ERROR];

  if($p.userReceiveUpdates != null && $p.userReceiveUpdates != undefined) {

    state(pathToInstance, "hide");

    return true;

  } else {

    state(pathToInstance, "show");

    text([FORM_PAGE, UPDATES_ERROR, "show", "text"], "Please choose an option");

    return false;

  }

}

checkTerms = function($p) {

  console.log("checkTerms", $p.userAcceptedTerms);

  const pathToInstance = [FORM_PAGE, TERMS_ERROR];

  if($p.userAcceptedTerms === true) {

    state(pathToInstance, "hide");

    return true;

  } else {

    state(pathToInstance, "show");

    text([FORM_PAGE, TERMS_ERROR, "show", "text"], "You must agree to the terms to continue");

    return false;

  }

}

checkForm = function($p) {

  console.log("checkForm");

  let formIsComplete = true;

  // check form elements

  if( !checkName() ) {

    formIsComplete = false;

  }

  if( !checkEmail() ) {

    formIsComplete = false;

  }

  if( !checkPassword() ) {

    formIsComplete = false;

  }

  if( !checkUpdates($p) ) {

    formIsComplete = false;

  }

  if( !checkTerms($p) ) {

    formIsComplete = false;

  }

  // if all form elements are complete then continue

  if(formIsComplete) {

    $p.userName = text([FORM_PAGE, NAME_INPUT, "default", "input"]);

    $p.userEmail = text([FORM_PAGE, EMAIL_INPUT, "default", "input"]);

    goto("Loading");

  }

}

// defines a function to be called by input components

onInputBlur = function(pathToInput) {

  const instanceName = pathToInput[1];

  console.log("onInputBlur:", pathToInput, instanceName);

  switch(instanceName) {

    case NAME_INPUT:

      checkName();

      break;

    case EMAIL_INPUT:

      checkEmail();

      break;

    case PASSWORD_INPUT:

      checkPassword();

      break;

    default:

      console.log("onInputBlur: instanceName not recognised:", instanceName);

  }

}

 

Step 6 (optional): Add a script to focus the first text input

If you would like the first input field to focus automatically, add the following script to this action also:

// focus the first field
state([FORM_PAGE, NAME_INPUT], "focus");

 

Step 7: Add a trigger to the submit button

Finally, we want to trigger the validation of all of the scripts. Add the following script to run on a click/tap of the submit / sign up button:

checkForm($p);

 

Step 8: Time to preview!

Select preview, and check out the prototype in action!

You can also apply your own custom styling to the components. For example, you may prefer the focus border to be green. To do this, go to the components tab of your project, and open the component. Or within the editor, double click on an instance to open it. 

Did this answer your question?