Handle modal form generated by Ajax using promises

Viewed 151

I'm pretty new to Ajax calls and promises, and I'm trying to load a form in a modal once the user clicks the sign up button on my app. To do that, I dynamically load the form with an Ajax call using the fetch() API. The fetch API calls the endpoint /accounts/signup which renders my HTML form:

header.html

<body>
        <div class="container">
            <header class="header">
                <!-- modal window -->
                <div class="modal hidden">
                        <button class="close-modal">&times;</button>
                        
                        <p class="modal-text">
                           
                        </p>
                </div>
                <!-- End of modal window -->

/accounts/signup

<h2>Sign up by filling in the form:</h2>
<form method="POST" id="signup-form">
  {% csrf_token %}
  {% for field in form %}
    <p>
      {{ field.label_tag }}<br>
      {{ field }}
      {% if field.help_text %}
        <small style="color: grey">{{ field.help_text }}</small>
      {% endif %}
      {% for error in field.errors %}
        <p style="color: red">{{ error }}</p>
      {% endfor %}
    </p>
  {% endfor %}
  <button type="submit" id="signup-submit">Sign up</button>
</form>

Ajax call:

const getForm = function(endpoint) { 
    const request = fetch(endpoint)
        .then(response => response.text())
        .then(data => modal.innerHTML = data) 
}

btnSignup.addEventListener('click', function(){
    getForm('/accounts/signup')
    document.getElementById('signup-form').addEventListener('load', function(e){
        e.preventDefault()
    })
})

As you can see I'm successfully loading the HTML form on the modal using promises. The issue I have is that after rendering the form I want to handle it adding preventDefault() and sending it with another Ajax call. When I try to select the form and add i.e. preventDefault(), I am not able to do that.

The error I get is:

Uncaught TypeError: Cannot read property 'addEventListener' of null
    at HTMLAnchorElement.<anonymous>

Which is basically JS not finding the id="signup-form" which was dynamically loaded on the page. I get the same error if I try to chain another .then() method and perform the task there:

const getForm = function(endpoint) { 
    const request = fetch(endpoint)
        .then(response => response.text())
        .then(data => modal.innerHTML = data) 
        .then(console.log(document.getElementById('signup-form')))
}

btnSignup.addEventListener('click', function(){getForm('/accounts/signup')})

How would you usually handle such use cases? What is the best practice when it comes to handling dynamically loaded HTML forms with Ajax and promises?

1 Answers

Your code is trying to access the value of btnSignup at the time the script is executed, which is before the ajax call is made. At that time the variable is undefined, which is what the error tells you.

You can only add events to elements after they have been created. This is probably closer to what you're trying to achieve:

const getForm = function(endpoint) {
  fetch(endpoint)
    .then(response => response.text())
    .then(data => modal.innerHTML = data) 
    .then(() => {
       const signupForm = document.getElementById('signup-form');
       if (signupForm) {

          // here signupForm is a DOM element 
          // this code runs after fetch() has resolved

          console.log(signupForm);
          signupForm.addEventListener('submit', function(e){
            // prevents default on the form's `submit`
            e.preventDefault();
          })
       }
    });

  // here signupForm is `undefined` 
  // you are outside of the callback function
  // which means code placed here will run immediately
  // after fetch() has left, but before it has returned, 
  // regardless of how fast the response comes back

}

// here signupForm is `undefined` 
// you are outside of the `getForm` function
// which means code placed here will run when the page parses this script
 

The above is meant as a correct syntax example, more than anything else.
Make sure the right elements are being targeted and that the actual business logic maps onto what your script is supposed to achieve. console.log() is your best friend.

Side-note: calling .preventDefault() on a load event of a <form> element is, most likely, not what you intend to do. You probably want to call this method on the submit event but, then again, the end result of your script is not really clear.

I'd also advise against naming a variable holding a <form> DOM element as btnSignup. When naming, try to limit the potential confusion around later understanding the role of each variable. In the long run, it will allow you to handle more complex routines easier.

Related