2022 Web Development Bootcamp

Section 28: Ajax & JavaScript - driven HTTP Requests - Sending Requests "Behind The Scenes"

olivia_yj 2022. 9. 27. 20:25

The goals

💪🏻What & Why?

✌🏻Sending JS-driven Http Requests

👍🏻Handling Responses & Errors

 

What is "Ajax"?

Asynchronous JavaScript And XML

 

XMLHttpRequest

What is XML? 

Extensible Markup Language (XML)

XML is a data format for formatting / structuring text data in a machine-readable way

Looks like HTML -  because HTML is based on XML

HTML is standardized, XML is not

Nowadays, XML isn't really used anymore for transmitting data - it's too verbose.

Instead, JSON (JavaScript Object Notation) is used

Like JavaScript objects, but all keys (property names) are between double quotes, all text values are between double quotes. Other value types like numbers, booleans or nested arrays and objects are also supported.

 

Now let's fix our code using Ajax

  <section id="comments">
        <% if (!comments) { %>
        <p>
          This post might have comments. You can load them if you want to view
          them.
        </p>
        <form action="/posts/<%= post._id %>/comments" method="GET">
          <button class="btn btn-alt">Load Comments</button>
        </form>
        <% } else if (comments.length === 0) { %>
          <p>No comments found.</p>
        <% } else { %>
          <ol>
            <% for (const comment of comments) { %>
              <li>
                <%- include('includes/comment-item', { comment: comment }) %>
              </li>
            <% } %>
          </ol>
        <% } %>
   </section>

So here we have the form code to send data and get new url

Since it's ineffcient, let's fix this code with ajax

 

<section id="comments">
        <% if (!comments) { %>
        <p>
          This post might have comments. You can load them if you want to view
          them.
        </p>
        <button id="load-comments-btn" class="btn btn-alt" data-postid="<%= post._id %>">Load Comments</button>
        <% } else if (comments.length === 0) { %>
          <p>No comments found.</p>
        <% } else { %>
          <ol>
            <% for (const comment of comments) { %>
              <li>
                <%- include('includes/comment-item', { comment: comment }) %>
              </li>
            <% } %>
          </ol>
        <% } %>
      </section>

Just remove form and put an id in button to set eventlistener for it.

And make script folder and put some scripts in it.

 

const loadCommentsBtnElement = document.getElementById('load-comments-btn');

async function fetchCommentsForPost() {
  const postId = loadCommentsBtnElement.dataset.postid;
  const response = await fetch(`/posts/${postId}/comments`);
  const responseData = await response.json();
  console.log(responseData);
}

loadCommentsBtnElement.addEventListener('click', fetchCommentsForPost);

Here we could have used 'XMLHttpRequest' to use XML or used third party 'axios'

But here we chose to use fetch, which is built-in.

And we should put text here as parameter, and we can set the url that we wanna send request

so we can use fetch like ('/posts/.../comments')

"/..." creates an absolute path that is automatically appended to the currently active page domain (like localhost:3000).

Alternatively, we coulld provide a full URL like 'localhost:3000/...'

In html code, we set 'data-' attribute to use dataset method,.

 

And this fetch returns 'promise' and to make it work asynchronously we can use async and await

 

json method is a built-in method to send json back.

I kept having some errors actuallly and I felt so annoyed by it..

But it was because in database.js I didn't change db name as mine so it couldn't bring the data from the database.

And the error code was 500

 

And now we will try to fix ejs code with using ajax to draw only the change, not reload whole page!

So at this post-detail page, we will remove all ejs code. And now we move to comments.js script file to bring the data from database and show it and only change that part.

 

const loadCommentsBtnElement = document.getElementById('load-comments-btn');
const commentsSectionElement = document.getElementById('comments');

function createCommentsList(comments) {
  const commentListElement = document.createElement('ol');
  
  for (const comment of comments) {
    const commentElement = document.createElement('li');
    commentElement.innerHTML = `
      <article class="comment-item">
        <h2>${comment.title}</h2>
        <p>${comment.text}</p>
      </article>
    `;
    commentListElement.appendChild(commentElement);
  }

  return commentListElement;
}

async function fetchCommentsForPost() {
  const postId = loadCommentsBtnElement.dataset.postid;
  const response = await fetch(`/posts/${postId}/comments`);
  const responseData = await response.json();
  
  const commentsListElement = createCommentsList(responseData);
  commentsSectionElement.innerHTML = '';
  commentsSectionElement.appendChild(commentsListElement);
}

loadCommentsBtnElement.addEventListener('click', fetchCommentsForPost);

So with this code, we can fetch the data from database and update only the data field not the whole page.

 

But when we write a comment and submit it then it reload the page which we don't want.

So now let's fix this also.

 

<body>
    <%- include('includes/header') %>
    <main id="post-detail">
      <h1><%= post.title %></h1>
      <section id="post-meta">
        <address>
          <a href="mailto:<%= post.author.email %>"><%= post.author.name %></a>
        </address>
        | <time datetime="<%= post.date %>"><%= post.humanReadableDate %></time>
      </section>
      <hr />
      <section>
        <p id="body"><%= post.body %></p>
      </section>
      <section id="comments">
        <p>
          This post might have comments. You can load them if you want to view
          them.
        </p>
        <button id="load-comments-btn" class="btn btn-alt" data-postid="<%= post._id %>">Load Comments</button>
      </section>
      <section id="comments-form">
        <h2>Leave a comment</h2>
        <form action="/posts/<%= post._id %>/comments" method="POST">
          <div class="form-control">
            <label for="title">Comment title</label>
            <input type="text" id="title" name="title" required />
          </div>
          <div class="form-control">
            <label for="text">Your comment</label>
            <textarea name="text" id="text" rows="3" required></textarea>
          </div>
          <button class="btn">Save Comment</button>
        </form>
      </section>
    </main>
  </body>

So this is our original code and here is a form we write our comment and submit.

Now we will remove this form's function to block the reloading of webpage.

And use ajax to send our data without reloading the page!

 

Event.preventDefault()

The preventDefault() method of the Event interface tells the user agent that if the event does not get explicitly handled, its default action should not be taken as it normally would be.

It prevent the browser default.

The event continues to propagate as usual, unless one of its event listeners calls stopPropagation() or stopImmediatePropagation(), either of which terminates propagation at once.

As noted below, calling preventDefault() for a non-cancelable event, such as one dispatched via EventTarget.dispatchEvent(), without specifying cancelable: true has no effect.

 

 

 

 

 

 

 

Sources

https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault

 

Event.preventDefault() - Web APIs | MDN

The preventDefault() method of the Event interface tells the user agent that if the event does not get explicitly handled, its default action should not be taken as it normally would be.

developer.mozilla.org