Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

JavaScript AJAX Basics Programming AJAX Programming AJAX Solution

Is there a way to improve the code and apply the DRY principle?

For example, create one function for executing the XMLHttpRequest object, and one callback function for each AJAX task.

Max Datskovsky
Max Datskovsky
10,371 Points

I changed the JSON files a bit, to be more standard.

Here's my widget.js file:

const widget = (filename, divID) => {
    let xhr = new XMLHttpRequest();
    xhr.onreadystatechange = () => {
        if (xhr.readyState == 4 && xhr.status == 200) {
            let data = JSON.parse(xhr.responseText);
            let html = "<ul class='bulleted'>";

            data.forEach(element => {html += `<li class='${element.available?"in":"out"}'>${element.name}<li>`});

            html += "</ul>";
            document.getElementById(divID).innerHTML = html;
        }
    };
    xhr.open("GET", filename);
    xhr.send();
}
widget("./data/rooms.json", "roomList")
widget("./data/employees.json", "employeeList")

4 Answers

Tobias Graefe
Tobias Graefe
11,934 Points

Sure! You can create a functions, that takes the keys and classes as input parameter and reuse it in your callback. Same thing for the reauest routine. Than you simply have to pass in the request object and the data path.

Jan Durcak
Jan Durcak
12,662 Points
const newone = new XMLHttpRequest();
newone.onreadystatechange = function() {                                   
     if (newone.readyState === 4){
       const rooms = JSON.parse(newone.responseText);
      const roomList = document.getElementById('roomList');

      let ul = '<ul class="rooms">';
      rooms.forEach( r => {
         ul += `<li class=${r.available ? "full" : "empty"}>${r.room}</li>`;
      });
      ul += '</ul>';
      roomList.innerHTML = ul;
          }
        };

      newone.open('GET', 'data/widget.json');
      newone.send();
Trevor Maltbie
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Trevor Maltbie
Full Stack JavaScript Techdegree Graduate 17,004 Points

If you use the conditional ternary operator you can make this code simpler too.

rooms[i].available ? roomsHTML += '<li class="empty">' : roomsHTML += '<li class="full">'
Andre Hammons
Andre Hammons
9,264 Points

Here's my attempt to apply the DRY principle.

/* ===========================================
              COMMON FUNCTIONS
=========================================== */

function sendRequest(request, method, json) {
  request.open(method, json);
  request.send();
};

function getValueFromBool(obj=null, prop='', trueVal=null, falseVal=null) {
  // Returns value based on boolean property.
  if (obj[prop] === true) {
    return trueVal;
  } else {
    return falseVal;
  };
};


function createList(jsonData=null, liText='', ulClass='', boolData=null) {
  // Create unordered list and assign class.
  const ul = document.createElement('ul')
  ul.className = ulClass;
  // Loop through parsed JSON to create list items.
  for (let obj of jsonData) {
    const li = document.createElement('li')
    li.textContent = obj[liText];
    li.className = getValueFromBool(obj, ...boolData);
    ul.appendChild(li);
  };
  return ul;
};


/* ===========================================
      EMPLOYEE OFFICE STATUS REQUEST
=========================================== */

const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
  // Gatekeeper
  if (xhr.readyState !== 4) {
    return;
  };
  const employees = JSON.parse(xhr.responseText);
  // Select appropriate element to insert list.
  const officeStatus = document.getElementById('employeeList');
  // Create the list and append.
  const employeeList = createList(
    jsonData=employees, 
    liText='name', 
    ulClass='bulleted', 
    boolData=['inoffice', 'in', 'out']
  );
  officeStatus.appendChild(employeeList);
};
sendRequest(xhr, 'GET', 'data/employees.json');


/* ===========================================
        MEETING ROOM STATUS REQUEST
=========================================== */

const xhr2 = new XMLHttpRequest();
xhr2.onreadystatechange = () => {
  // Gatekeeper
  if (xhr2.readyState !== 4) {
    return;
  };
  const rooms = JSON.parse(xhr2.responseText);
  // Select appropriate element to insert list.
  const roomStatus = document.getElementById('roomList');
  // Create the list and append.
  const roomList = createList(
    jsonData=rooms,
    liText='room',
    ulClass='rooms',
    boolData=['available', 'empty', 'full']
  );
  roomStatus.appendChild(roomList);
};
sendRequest(xhr2, 'GET', 'data/rooms.json');