The video

Because of its complexity and the amount of stuff I have to explain I have decided to do a video guide for this lab. You can still find all the code you need in this post but for in depth explanations you have to follow this link.

Links to the stuff I talk about

The HTML

At the time of solving this lab, the example was very buggy and I had to work around it. The HTML file was probably changed since then to get rid of the bugs. Here is the file I used:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Lab 9 - AJAX</title>
    <link rel="stylesheet" href="./lab9_username.css">
</head>
<body>
    <div class="content--container">
        <h1>Grade 3</h1>
        <div class="users--container">
            <h2>Users</h2>
            <div id="users_response"></div>
            <button id="get_users">Get Users</button>
        </div>
        
        <div class="posts--container">
            <h2>Posts</h2>
            <div id="posts_response"></div>
            <button id="get_posts">Get Posts</button>
        </div>

        <div class="selected_user_post--container">
            <h2>Get post by selected user</h2>
            <select name="users" class="users_dropdown"></select>
            <button id="get_user_post">Get user post</button>
        </div>

        <h1>GRADE 4</h1>
        <div class="create_post--container">
            <h2>Create new post</h2>
            <form action="POST" id="create_post_form">
                <label for="">Select user</label>
                <select name="users" class="users_dropdown"></select>
                <label for="">Title</label>
                <input type="text" name="post_title" id="post_title">
                <label for="">Body text</label>
                <textarea name="body" id="post_body"></textarea>
                <button id="create_post">Create new post</button>
            </form>

            <div class="created_post--container">
                
            </div>
        </div>

        <h1>GRADE 5</h1>
        <div class="update_post--container">
            <h2>Update Post</h2>
            <form action="POST" id="update_post_form">
                <input type="hidden" id="update_id" value="">
                <input type="hidden" id="update_user_id" value="">
                <label for="">Title</label>
                <input type="text" id="update_post_title">
                <label for="">Body</label>
                <textarea name="update_body" id="update_body"></textarea>
                <button id="update_post">Update post</button>
            </form>

            <div id="updated_post--container"></div>

        </div>

    </div>
    <script src="./lab9_username.js"></script>
</body>
</html>

The Javascript

let posts = []

async function getUsers () {
  const users = await fetchUsers()
  renderUsers(users)
  populateUsersDropdowns(users)
}

async function getPosts () {
  posts = await fetchPosts()
  renderPosts()
}

async function getUserPost () {
  const userId = document.querySelectorAll('.users_dropdown')[0].value

  const post = await fetchUserPost(userId)
  posts.push(post)
  renderPosts()
}

async function createNewPost (e) {
  e.preventDefault()

  const userId = document.querySelectorAll('.users_dropdown')[1].value
  const title = document.querySelector('#post_title').value
  const body = document.querySelector('#post_body').value

  const newPost = await fetchNewPost(userId, title, body)
  posts.push(newPost)
  renderPosts()

  document.querySelector('#post_title').value = ''
  document.querySelector('#post_body').value = ''

  const createdPostContainer = document.querySelector('.created_post--container')
  createdPostContainer.innerHTML = ''

  const notification = document.createElement('h2')
  notification.textContent = `Your post "${newPost.title}" has been created`
  createdPostContainer.appendChild(notification)
}

async function updatePost (e) {
  e.preventDefault()

  const postId = document.querySelector('#update_id').value
  const userId = document.querySelector('#update_user_id').value
  const title = document.querySelector('#update_post_title').value
  const body = document.querySelector('#update_body').value

  const updatedPost = await fetchUpdatedPost(postId, userId, title, body)
  
  const postIndex = posts.findIndex(post => {
    return post.id === updatedPost.id
  })

  posts[postIndex] = updatedPost

  renderPosts()

  document.querySelector('#update_id').value = ''
  document.querySelector('#update_user_id').value = ''
  document.querySelector('#update_post_title').value = ''
  document.querySelector('#update_body').value = ''

  const updatedPostContainer = document.querySelector('#updated_post--container')
  updatedPostContainer.innerHTML = ''

  const notification = document.createElement('h2')
  notification.textContent = `Your post "${updatedPost.title}" has been updated`
  updatedPostContainer.appendChild(notification)
}

function selectPostToUpdate () {
  const post = posts.find(post => {
    return post.id === Number(this.dataset.id)
  })

  document.querySelector('#update_id').value = post.id
  document.querySelector('#update_user_id').value = post.userId
  document.querySelector('#update_post_title').value = post.title
  document.querySelector('#update_body').value = post.body
}

async function fetchUsers () {
  const response = await fetch('https://jsonplaceholder.typicode.com/users')
  const data = await response.json()
  return data
}

async function fetchUserPost (userId) {
  const response = await fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`)
  const data = await response.json()
  return data[0]
}

async function fetchPosts () {
  const response = await fetch('https://jsonplaceholder.typicode.com/posts')
  const data = await response.json()
  return data.slice(0, 10)
}

function renderUsers (users) {
  const userContainer = document.querySelector('#users_response')

  users.forEach(user => {
    const name = document.createElement('h3')
    name.textContent = user.name
    userContainer.appendChild(name)

    const email = document.createElement('p')
    email.textContent = user.email
    userContainer.appendChild(email)
  })
}

function populateUsersDropdowns (users) {
  const dropdowns = document.querySelectorAll('.users_dropdown')

  dropdowns.forEach (dropdown => {
    users.forEach(user => {
      const option = document.createElement('option')
      option.textContent = user.name
      option.value = user.id
  
      dropdown.insertAdjacentElement('afterbegin', option)
    })
  })
}

function renderPosts () {
  const postContainer = document.querySelector('#posts_response')
  postContainer.innerHTML = ''

  posts.forEach(post => {
    const title = document.createElement('h3')
    title.textContent = post.title
    postContainer.appendChild(title)

    const body = document.createElement('p')
    body.textContent = post.body
    postContainer.appendChild(body)

    const editButton = document.createElement('button')
    editButton.textContent = 'Edit'
    editButton.dataset.id = post.id
    editButton.dataset['user_id'] = post.userId
    editButton.addEventListener('click', selectPostToUpdate)
    postContainer.appendChild(editButton)
  })
}

async function fetchNewPost (userId, title, body) {
  const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
    method: 'POST',
    body: JSON.stringify({
      userId: userId,
      title: title,
      body: body,
    }),
    headers: {
      "Content-type": "application/json; charset=UTF-8"
    }
  })
  const data = await response.json()
  return data
}

async function fetchUpdatedPost (postId, userId, title, body) {
  const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${postId}`, {
    method: 'PUT',
    body: JSON.stringify({
      id: postId,
      title: title,
      body: body,
      userId: userId
    }),
    headers: {
      "Content-type": "application/json; charset=UTF-8"
    }
  })
  const data = await response.json()
  return data
}


document.querySelector('#get_users').addEventListener('click', getUsers)
document.querySelector('#get_posts').addEventListener('click', getPosts)
document.querySelector('#create_post_form').addEventListener('submit', createNewPost)
document.querySelector('#update_post_form').addEventListener('submit', updatePost)
document.querySelector('#get_user_post').addEventListener('click', getUserPost)