Search code examples
javascripthtmlgrid-layout

Displaying array of objects dynamically in table columns - Javascript


I am attempting to create an appointment slot selection grid. I have all the appointments grouped into dates. I want to display the array of Objects below as a clickable grid. I have everything I need but I'm having difficulty displaying it in columns. My current output: enter image description here

As you can see it is displaying the correct appointments next to the correct date header however its adding the next one in the same row and so on. I want each date and its corresponding appointments to be displayed in separate columns. Date header at the top with appointments listed below. I'm just using a main.js file and an index.html file.

Grouped appointment slots array below looks something like this:

2020-09-25: [{0:{AppointmentDateTime: "2020-09-25T13:00:00Z"}}]
2020-09-28: [{0:{AppointmentDateTime: "2020-09-28T08:00:00Z"}},{1:{AppointmentDateTime: "2020-09-28T10:30:00Z"}}, {2:{AppointmentDateTime: "2020-09-28T11:00:00Z"}}]
2020-09-29: [{0:{AppointmentDateTime: "2020-09-29T08:00:00Z"}},{1:{AppointmentDateTime: "2020-09-29T09:00:00Z"}}, {2:{AppointmentDateTime: "2020-09-29T11:00:00Z"}}]

Here is what I have so far:

let result = await response.json();
let groupedAppointments = {}

result.forEach((item) => {
    const date = item.AppointmentDateTime.split('T')[0]
    if (groupedAppointments[date]) {
        groupedAppointments[date].push(item);
    } else {
        groupedAppointments[date] = [item];
    }
})


var grid = clickableGrid(groupedAppointments, groupedAppointments, function(el, item) {
    console.log("You clicked on element:", el);
    selectedAppointment = item.AppointmentDateTime;
    console.log(selectedAppointment);
    el.className = 'clicked';
    if (lastClicked) lastClicked.className = '';
    lastClicked = el;
});

document.getElementById("clickable-grid").appendChild(grid);
function clickableGrid(groupedAppointments, index, callback) {
    var i = 0;
    var grid = document.createElement('table');

    grid.className = 'grid';

    Object.keys(groupedAppointments).forEach((item) => {

        var days = groupedAppointments[item];
        var tableRowHeader = grid.appendChild(document.createElement('tr'));
        var th = tableRowHeader.appendChild(document.createElement('th'));
        th.innerHTML = item;

        days.forEach((item) => {

            var tr = grid.appendChild(document.createElement('tr'));
            //var rowHeader = tr.appendChild(document.createElement('th'))
            var cell = tr.appendChild(document.createElement('td'));
            //rowHeader.innerHTML = item.SlotName;
            cell.innerHTML = item.AppointmentDateTime;
            cell.addEventListener('click', (function(el, item) {
                return function() {
                    callback(el, item);
                }
            })(cell, item), false);
        })

    })

    return grid;
}

I hope this is enough detail. Any questions just ask. All help greatly appreciated.

updated output:

enter image description here

enter image description here


Solution

  • You shouldn't create a tr for each appointment.

    You need two loops. One loop creates the header row with all the dates.

    Then you loop over the array indexes to create the data rows. Within that, you have a nested loop for each date, filling in that column in the row. Since dates will have different numbers of appointments, you need to check if the current date has that many appointments. If it is you fill in the cell, otherwise you leave it empty.

    function clickableGrid(groupedAppointments, callback) {
      var i = 0;
      var grid = document.createElement('table');
    
      grid.className = 'grid';
      var longest = 0;
      var headerRow = grid.appendChild(document.createElement('tr'));
      Object.entries(groupedAppointments).forEach(([item, day]) => {
        if (day.length > longest) {
          longest = day.length;
        }
        var th = headerRow.appendChild(document.createElement('th'));
        th.innerHTML = item;
      });
      for (let i = 0; i < longest; i++) {
        var tr = grid.appendChild(document.createElement('tr'));
        Object.values(groupedAppointments).forEach(item => {
          var cell = tr.appendChild(document.createElement('td'));
          if (i < item.length) {
            let time = item[i].AppointmentDateTime.split('T')[1].split('Z')[0];
            cell.innerHTML = time;
            cell.addEventListener('click', (function(el, item) {
              return function() {
                callback(el, item);
              }
            })(cell, item[i]), false);
          }
        });
      }
    
      return grid;
    }
    
    var data = {
      "2020-09-25": [{
        AppointmentDateTime: "2020-09-25T13:00:00Z"
      }],
      "2020-09-28": [{
        AppointmentDateTime: "2020-09-28T08:00:00Z"
      }, {
        AppointmentDateTime: "2020-09-28T10:30:00Z"
      }, {
        AppointmentDateTime: "2020-09-28T11:00:00Z"
      }],
      "2020-09-29": [{
        AppointmentDateTime: "2020-09-29T08:00:00Z"
      }, {
        AppointmentDateTime: "2020-09-29T09:00:00Z"
      }, {
        AppointmentDateTime: "2020-09-29T11:00:00Z"
      }]
    };
    
    document.body.appendChild(clickableGrid(data, function(cell, date) {
      console.log("You clicked on " + date.AppointmentDateTime);
    }));