How to create SFMC forms faster with Emmet, Alpine.js and Simple CSS

How to create SFMC forms faster with Emmet, Alpine.js and Simple CSS

This article explains how to create Salesforce Marketing Cloud forms faster using Alpine.js and Simple CSS frameworks.

Ain’t nobody got time for that

Who has time to create custom Marketing Cloud forms nowadays? Nobody, that’s who!

That’s why the web communities and creators have provided us with various frameworks, shortcuts and templates.

Let’s have a look at some of them and learn how to create forms fast!

Learn your shortcuts

First step in starting to write code faster is to learn the code editor shortcuts!

Whatever code editor you are using, there are always shortcuts to make our life a lot easier when writing hundreds or thousands lines of code.

VS Code, for example, provides the so-called “Cheat Sheets” with the default key bindings:

Download VS Code Cheat Sheet (Windows)
Download VS Code Cheat Sheet (Mac)

Write HTML faster with Emmet

If you don’t know what Emmet is, you are going to be amazed!

In a nutshell, Emmet is a code editor plugin that allows to generate HTML using CSS selector’s syntax. And the good news is: it’s pre-installed in VS Code!

How does it work? For example, typing the following code:

html:5>form:post>input:text+input:email+button:submit

Will produce this result:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <form action="" method="post">
        <input type="text" name="" id="">
        <input type="email" name="" id="">
        <button type="submit"></button>
    </form>
</body>
</html>

Interested? Don’t wait, download the Cheat Sheet, print it, hang it on the wall and be golden:

Download Emmet Cheat Sheet

Build interactive SFMC forms faster

Let’s build a Marketing Cloud custom form and learn how to do it even faster!

HTML and CSS

At first, let’s use our friend Emmet to generate the HTML code:

html:5>h1+form>(label+input:text)*3+input:checkbox+label+button:submit

Then add some Ids, classes and attributes to configure the form’s properties.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>

    <h1>Subscribe to our Newsletter</h1>

    <form>

        <label for="firstname">First name</label>
        <input 
            required
            minlength="2" 
            id="firstname" 
            type="text"
        >

        <label for="lastname">Last name</label>
        <input 
            required
            minlength="2" 
            id="lastname" 
            type="text"
        >

        <label for="email">Email address</label>
        <input 
            required
            pattern="^\S+@\S+\.\S+$" 
            id="email" 
            type="email"
        >

        <input 
            required
            id="consent" 
            type="checkbox"
        >
        <label for="consent">
            I've read and understood the <a href="#">privacy policy</a>
        </label>

        <button type="submit">Subscribe me</button>

    </form>

</body>
</html>

Works fine! But our form doesn’t look good, as we need to create some styles to decorate the input fields, the button, the title, make the form responsive for mobile devices…

Again, we don’t have time for that!!!

Instead, we could use a CSS framework, such as Simple CSS (created by Kev Quirk). Like it? Buy him A Coffee

In order to make it work, just attach the CDN link in the HTML header and you are good to go!

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
</head>

Mmmmmm… Beautiful isn’t it? Let’s move on, we don’t have all day.

JavaScript

In order to add interactiveness to our form, we need to write some JavaScript: get every input one-by-one, assign them to a variable, create an event listener for the form that prevents the default submission process…

No! This will take too long!!!

Instead, we are going to be using a JavaScript framework called Alpine.js and cut our development time by 2x or even 3x.

Just like with Simple CSS, we only need a CDN link in the header to make it work:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.jsdelivr.net/npm/alpinejs@3.12.1/dist/cdn.min.js" defer></script>
    <link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
</head>

How does Alpine work?

If you are familiar with JavaScript frameworks, such as Vue, React or Angular, you will understand how it works in about a minute.

But if you are new to the concept, let’s have a quick overview:

Instead of creating selectors and listeners for different HTML elements, all we need to do is to create a single function that returns an object, that can be made of sub-objects, functions and arrays.

For the sake of consistency and clarity, let’s make the returned object hold 2 entities: the values provided in the form (line 6) and the submit function that’s triggered when the button under the form is clicked (line 12).

<script>

    function SubscribeForm() {

      return {
        form: {
          firstname: "",
          lastname: "",
          email: "",
          consent: false
        },
        submit() {
          console.log(this.form);
        }
      };

    }

</script>

Now, all we have to do to attach this function to our HTML form and make it interactive is to add Alpine-specific attributes with the appropriate values:

x-data: defines a chunk of HTML as an Alpine component and provides the reactive data for that component to reference.
x-model: allows you to bind the value of an input element to Alpine data.
x-show: provides an expressive way to show and hide DOM elements.
x-ref: useful utility for easily accessing DOM elements directly.
x-cloak: addresses this scenario by hiding the element it’s attached to until Alpine is fully loaded on the page.
@click: is a shortcut for x-on:click, that allows you to easily run code on dispatched DOM events. It can be concatenated with the keyword prevent in order to implicitly add event.preventDefault() function before the event is triggered.

Let’s have a look at our code with Alpine-specific attributes:

<form
    x-data="SubscribeForm()" 
    @submit.prevent="submit"
>

    <label for="firstname">First name</label>
    <input 
        required
        minlength="2" 
        id="firstname" 
        type="text"
        x-model="form.firstname" 
    >

    <label for="lastname">Last name</label>
    <input 
        required
        minlength="2" 
        id="lastname" 
        type="text"
        x-model="form.lastname" 
    >

    <label for="email">Email address</label>
    <input 
        required
        pattern="^\S+@\S+\.\S+$" 
        id="email" 
        type="email"
        x-model="form.email" 
    >

    <hr>

    <input 
        required
        id="consent" 
        type="checkbox"
        x-model="form.consent" 
    >
    <label for="consent">
        I've read and understood the <a href="#">privacy policy</a>
    </label>

    <br>

    <button type="submit">Subscribe me</button>

</form>

What about form validation?

Don’t worry about the form validation, we don’t have time to worry… the native HTML5 form validation in your browser is taking care of it, as long as the attributes required and pattern are referenced on the input fields.

SFMC flow

Enough chitchat! We are finally ready to build our simple interactive form!

As usual, we are going to be using the Form Handler technique and transfer the data from the form to the processing page using an async HTTP request (in this example, Fetch API).

What we want to achieve is the following:

  1. Form is filled out with valid data and the submit button is pushed.
  2. Data is sent to the Form Handler page using an async HTTP request.
  3. When the Form Handler responds, display a success or error message.

Cloud page

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.jsdelivr.net/npm/alpinejs@3.12.1/dist/cdn.min.js" defer></script>
    <link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
</head>
<body>

    <h1>Subscribe to our Newsletter</h1>

    <form
        x-data="SubscribeForm()" 
        x-ref="form"
        x-cloak
        @submit.prevent="submit"
    >

        <fieldset
            :disabled="disabled"
            x-show="!message"
        > 

            <label for="firstname">First name</label>
            <input 
                required
                minlength="2" 
                id="firstname" 
                type="text"
                x-model="form.firstname" 
            >

            <label for="lastname">Last name</label>
            <input 
                required
                minlength="2" 
                id="lastname" 
                type="text"
                x-model="form.lastname" 
            >

            <label for="email">Email address</label>
            <input 
                required
                pattern="^\S+@\S+\.\S+$" 
                id="email" 
                type="email"
                x-model="form.email" 
            >

            <hr>

            <input 
                required
                id="consent" 
                type="checkbox"
                x-model="form.consent" 
            >
            <label for="consent">
                I've read and understood the <a href="#">privacy policy</a>
            </label>

            <br>

            <button type="submit">Subscribe me</button>

        </fieldset>

        <p class="notice" x-text="message" x-show="message"></p>

    </form>

    <script>

        function SubscribeForm() {
    
          return {
            form: {
              firstname: "",
              lastname: "",
              email: "",
              consent: false
            },
            message: "",
            disabled: false,
            submit() {

                this.disabled = true;
                
                fetch("https://mydomain.com/form-handler", {
                    method: "POST",
                    body: JSON.stringify(this.form),
                }).then((data) => {
                    this.message = "Form successfully submitted.";
                }).catch((error) => {
                    this.message = "Something went wrong.";
                    console.error(error);
                }).finally(() => {
                    this.disabled = false;
                    this.$refs.form.reset();
                });

            }
          };
    
        }
    
    </script>

</body>
</html>

Form handler

<script runat='server'>

    Platform.Load('core', '1');
    
    var api = new Script.Util.WSProxy();

    try {    
    
        var form = Platform.Function.ParseJSON(
            Platform.Request.GetPostData()
        );
    
        var req = api.createItem('DataExtensionObject', {
            Name: "MyDataExtension",
            Properties: wsPack(form)
        });

        if(req.Status != "OK") {
            throw req.Results[0].ErrorMessage
        }

        Write(Stringify({ 
            status: 200, 
            result: result 
        }));
    
    } catch(error) {
    
        Write(Stringify({ status: 500, message: error }));
    
    } 
    
    function wsPack(o) {
    
        var res = [];
    
        for (var k in o) {
            res.push({ Name: k, Value: o[k] });
        }
    
        return res;
    }

</script>

Result

Now that our form is finished, let’s have a look at the result!

Conclusion

That’s how you create interactive forms fast and without a fuss!

Hope that the time you’ve spend reading this article will enable you to save more of your time in the future.

After all, we all need a coffee break!

ampscript
Up Next:

How to retrieve and parse JSON files with AMPscript and SSJS in SFMC

How to retrieve and parse JSON files with AMPscript and SSJS in SFMC