This article builds on Part 1 of this series, where a custom JSSP API was built to expose recipient data as JSON. Here, we use that API endpoint to dynamically load recipient data into an Adobe Campaign WebApp form using AJAX — without a page reload.

Dependencies

  • bootstrap@5.1.3
  • jquery-3.6.0.min

WebApp HTML

Paste the HTML form code into a Page activity within your WebApp. The form contains an input field for the recipient ID, a search button, and a set of fields that will be populated dynamically by the AJAX response.

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <!-- JS -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <title>Recipient Ajax Form</title>
    <style type="text/css">
      body {
        font-family: 'Open Sans', Arial, helvetica, sans-serif;
        color: #333333;
        background-color: #F5F7FA;
      }

      .readonly {
        cursor: pointer;
      }

      .send {
        display: none;
        max-width: 30px;
        text-align: center;
        padding: 0px;
        font-size: small;
        color: #acacac;
      }

      input[type='checkbox'] {
        accent-color: aquamarine;
      }

      .text-left {
        text-align: left;
      }

      button#input16619995299504 {
        display: none;
      }

      .container {
        background-color: white;
        padding: 20px 50px 20px 50px !important;
        box-shadow: 0 0px 5px 5px #f3f3f3;
      }
    </style>
  </head>
  <body style="" class="">
    <div class="container px-4">
      <div class="row text-center gx-5">
        <div class="col p-1">
          <h1>Ajax recipient form</h1>
        </div>
      </div>
      <div class="row text-center gx-5">
        <div class="col-2 p-1"></div>
        <div class="col p-1"></div>
      </div>
      <form id="searchIds" autocomplete="off">
        <div class="row gx-5">
          <div class="col-2 p-1">
            <b>Adobe ID</b>
          </div>
          <div class="col p-1">
            <input name="recipientId" class="form-control" id="recipientId" required="" type="text" value="" />
          </div>
          <div class="col-1 p-1">
            <input id="search" type="submit" value="Search" />
          </div>
        </div>
      </form>
      <form id="recipientData" autocomplete="off">
        <div class="row gx-5">
          <div class="col-2 p-1">
            <span style="text-decoration: underline;">
              <b>RECIPIENT DETAILS:</b>
            </span>
          </div>
          <div class="col p-1">
            <input name="lastModified" class="form-control readonly" id="lastModified" style="text-align: left; font-size: x-small; background: white; border: 0; cursor: auto;" type="text" readonly="readonly" />
          </div>
        </div>
        <div class="row gx-5 align-items-center">
          <div class="col-2 p-1">First Name</div>
          <div class="col p-1">
            <input name="firstName" class="form-control readonly" id="firstName" type="text" readonly="readonly" />
          </div>
        </div>
        <div class="row gx-5 align-items-center">
          <div class="col-2 p-1">Last Name</div>
          <div class="col p-1">
            <input name="lastName" class="form-control readonly" id="lastName" type="text" readonly="readonly" />
          </div>
        </div>
        <div class="row gx-5 align-items-center">
          <div class="col-2 p-1">Email Address</div>
          <div class="col p-1">
            <input name="email" class="form-control readonly" id="email" type="text" readonly="readonly" />
          </div>
        </div>
        <div class="row gx-5 align-items-center">
          <div class="col-2 p-1">Company</div>
          <div class="col p-1">
            <input name="company" class="form-control readonly" id="company" type="text" readonly="readonly" />
          </div>
        </div>
        <div class="row gx-5 align-items-center">
          <div class="col-2 p-1">Job Title</div>
          <div class="col p-1">
            <input name="jobTitle" class="form-control readonly" id="jobTitle" type="text" readonly="readonly" />
          </div>
        </div>
        <div class="row gx-5 align-items-center">
          <div class="col-2 p-1">Phone</div>
          <div class="col p-1">
            <input name="phone" class="form-control readonly" id="phone" type="text" readonly="readonly" />
          </div>
        </div>
        <div class="row gx-5 align-items-center">
          <div class="col-2 p-1">Origin</div>
          <div class="col p-1">
            <input name="origin" class="form-control readonly" id="origin" type="text" readonly="readonly" />
          </div>
        </div>
        <div class="row gx-5 align-items-center">
          <div class="col p-1">
            <span style="text-decoration: underline;">
              <b>CONTACT MANAGEMENT:</b>
            </span>
          </div>
          <div class="col p-1"></div>
          <div class="col p-1"></div>
          <div class="col-1 p-1"></div>
        </div>
        <div class="row gx-5 align-items-center">
          <div class="col-2 p-1">Double Opt-in</div>
          <div class="col p-1">
            <input name="doubleOptIn" class="form-control readonly" id="doubleOptIn" type="text" readonly="readonly" />
          </div>
        </div>
        <div class="row gx-5 align-items-center">
          <div class="col-2 p-1">Blacklist</div>
          <div class="col p-1">
            <input name="blacklist" class="form-control readonly" id="blacklist" type="text" readonly="readonly" />
          </div>
        </div>
        <div class="row gx-5 align-items-center">
          <div class="col p-1">
            <span style="text-decoration: underline;">
              <b>ADDRESS:</b>
            </span>
          </div>
          <div class="col p-1"></div>
          <div class="col p-1"></div>
          <div class="col-1 p-1"></div>
        </div>
        <div class="row gx-5 align-items-center">
          <div class="col-2 p-1">Address Line 1</div>
          <div class="col p-1">
            <input name="address1" class="form-control readonly" id="address1" type="text" readonly="readonly" />
          </div>
        </div>
        <div class="row gx-5 align-items-center">
          <div class="col-2 p-1">Address Line 2</div>
          <div class="col p-1">
            <input name="address2" class="form-control readonly" id="address2" type="text" readonly="readonly" />
          </div>
        </div>
        <div class="row gx-5 align-items-center">
          <div class="col-2 p-1">Address Line 3</div>
          <div class="col p-1">
            <input name="address3" class="form-control readonly" id="address3" type="text" readonly="readonly" />
          </div>
        </div>
        <div class="row gx-5 align-items-center">
          <div class="col-2 p-1">Address Line 4</div>
          <div class="col p-1">
            <input name="address4" class="form-control readonly" id="address4" type="text" readonly="readonly" />
          </div>
        </div>
        <div class="row gx-5 align-items-center">
          <div class="col-2 p-1">Postcode</div>
          <div class="col p-1">
            <input name="zipCode" class="form-control readonly" id="zipCode" type="text" readonly="readonly" />
          </div>
        </div>
        <div class="row gx-5 align-items-center">
          <div class="col-2 p-1">Country</div>
          <div class="col p-1">
            <input name="city" class="form-control readonly" id="city" type="text" readonly="readonly" />
          </div>
        </div>
        <div class="row gx-5 align-items-center">
          <div class="col-2 p-1"></div>
          <div class="col p-1"></div>
          <div class="col p-1"></div>
          <div class="col-1 p-1">
            <input name="send" id="send" type="submit" value="Submit" />
          </div>
        </div>
      </form>
    </div>
    <script type="text/javascript">
     $(document).ready(function() {
            checkErrors(); //check any errors on page load
          });
		
		  /* 
			 function which parses response data and loads it in the corresponding fields
			 this is possible by matching response keys with html form element fields names, they must have
			 the same name otherwise data won't be parse correctly.
		  */
          function addToForm(data) {
            Object.keys(data).forEach(key => document.querySelector(`input[name="${key}"]`) == null ? 0 : document.querySelector(`input[name="${key}"]`).value = data[key])
          }

          function checkErrors(arg) {
            var err = document.controller.getValue('/ctx/vars/err');
            var id = document.controller.getValue('/ctx/vars/id');
            if (err) {
              $('#neoErr').modal('show');
              $(".modal-body").append("<span class='errNeo'>" + err + "</span>");
            }
            if (arg == 1) {
              $(".errNeo").remove();
            }
          }
          var getDetails = function() {
            event.preventDefault(); 
            var id = document.getElementById("recipientId").value; //get recipient id from field
            var url = "/cus/recipientAPI.jssp?id=" + id; //url construct
            document.controller.setValue('/ctx/vars/id', id); //set recipient id to ctx context variable
            var request = $.ajax({
              type: 'GET',
              url: url,
              async: true,
              success: function(response) {
                $('#recipientData').trigger("reset");
                addToForm(response) //call function to map response data to field values
                /*** parse response errors ***/
                const errors = Object.keys(response).filter(k => k.startsWith('error')).map(k => response[k]);
                document.controller.setValue('/ctx/vars/err', errors);
                checkErrors();
              }
            });
          }
          /*** event listeners ***/
          var btn = document.getElementById("search"); btn.addEventListener('click', getDetails);
          
    </script>
    <!-- Modal -->
    <div tabindex="-1" class="modal fade" id="neoErr" aria-hidden="true" aria-labelledby="exampleModalLabel">
      <div class="modal-dialog modal-xl">
        <div class="modal-content">
          <div class="modal-header">
            <h5 class="modal-title">Error</h5>
            <button class="btn-close" id="input16619995299504" aria-label="Close" type="button" data-bs-dismiss="modal"></button>
          </div>
          <div class="modal-body"><%=ctx.vars.err%> </div>
          <div class="modal-footer">
            <button class="btn btn-secondary" id="input166199952995026" onclick="document.controller.setValue('/ctx/vars/err','');checkErrors(1);" type="button" data-bs-dismiss="modal">Close</button>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

JavaScript Logic (closer look)

The script calls the custom API endpoint, maps the response data to the form fields by matching JSON keys to HTML input name attributes, and handles errors gracefully.

<script type="text/javascript">

    $(document).ready(function() {
        checkErrors(); // Check for any errors on page load
    });

    // Event listener — trigger search on button click
    var btn = document.getElementById('search');
    btn.addEventListener('click', getDetails);

    /**
     * Maps API response keys to form input fields.
     * Field names in HTML must match response JSON keys exactly.
     */
    function addToForm(data) {
        Object.keys(data).forEach(function(key) {
            var input = document.querySelector('input[name="' + key + '"]');
            if (input !== null) {
                input.value = data[key];
            }
        });
    }

    /** Check for API errors stored in ctx vars */
    function checkErrors(arg) {
        var err = document.controller.getValue('/ctx/vars/err');
        var id  = document.controller.getValue('/ctx/vars/id');

        if (err) {
            $('#neoErr').modal('show');
            $('.modal-body').append(
                "<span class='errNeo'>" + err + "</span>"
            );
        }
        if (arg == 1) {
            $('.errNeo').remove();
        }
    }

    /** Call the API and load response data into the form */
    var getDetails = function() {
        event.preventDefault();

        var id  = document.getElementById('recipientId').value;
        var url = '/cus/recipientAPI.jssp?id=' + id;

        // Store recipient ID in ctx context variable
        document.controller.setValue('/ctx/vars/id', id);

        $.ajax({
            type:    'GET',
            url:     url,
            async:   true,
            success: function(response) {
                $('#recipientData').trigger('reset');
                addToForm(response);

                // Extract and store any error keys from response
                var errors = Object.keys(response)
                    .filter(function(k) { return k.startsWith('error'); })
                    .map(function(k) { return response[k]; });

                document.controller.setValue('/ctx/vars/err', errors);
                checkErrors();
            }
        });
    };

</script>

Expected Webapp Design (SPA)

HTML Form Render

Operation

  1. Type the internal ID of the recipient record you want to load into the search field.
  2. Click Search. The script constructs the API endpoint URL with the ID parameter and fires an AJAX request.
  3. If the recipient ID does not exist, an error modal is displayed. Otherwise, the response data is mapped directly into the form fields.

Suggested Enhancements

  • Modify the API to load data from custom schemas rather than the standard recipient schema.
  • Replace Adobe Campaign native validation with Bootstrap form validation for a more modern user experience.
  • Submit form data back to Campaign without a page refresh using AJAX POST.
https://martech.network/
Do you like David Garcia's articles? Follow on social!

Login

Welcome! we are excited to have you.

Martech.network is an inspiring community where marketing automation experts unite to exchange innovative ideas, tackle challenges, and elevate each other. Our mission is clear: Unite minds, ignite creativity, and empower growth through digital innovation.
Registration