Home > Software design >  How do I access items in a nested array, from an external JS file, in a For Loop?
How do I access items in a nested array, from an external JS file, in a For Loop?

Time:04-07

I am working on my own website that acts as a CV and portfolio, a fun way to display my web design skills. The aim of it is to use JavaScript to populate the webpage with each of my jobs, key skills, qualifications, etc. from arrays used as a database.

In my head section, I added two script tags

<script type="module" src="js/database_career.js" defer></script>
<script type="module" src="js/script.js" defer></script>

In the database_career.js file, I created a class constructor with the job details as its object properties

function Job(title,company,location,date,summary,details) {
  this.title = title;
  this.company = company;
  this.location = location;
  this.date = date;
  this.summary = summary;
  this.details = details;
}

And in that same file, here's (a sample of) an array of all the jobs I want to populate my web page with.

const JOBS = [
  new Job(
    "Job Title",
    "Company",
    "Location",
    "Month 20XX ~ Now",
    "Insert Job Summary Here",
    ["Bullet Point for notable task or accomplishment 1",
    "Bullet Point for notable task or accomplishment 2",
    "Bullet Point for notable task or accomplishment 3"]
  ),
  new Job()
];

Now here's what I coded in the main script.js file so far.

    import JOBS from "./database_career.js";
        
            // Populate the Career Section
            const myCareer = document.querySelector("#work");
              const jobSection = document.createElement("div");
                jobSection.classList.add("content-wrap", "clearfix");
            
                const jobsHeading = document.createElement("h2");
                const jobsHeading_content = `<i  aria-hidden="true"></i>Work Experience`;
                jobsHeading.innerHTML = jobsHeading_content;
                jobSection.append(jobsHeading);
        
        for (let i=0;i<JOBS.length;i  ) {
                const jobEntry = document.createElement("div");
                  jobEntry.classList.add("entry");
        
                const jobContent_narrow = `
                  <div >
                    <h3>${JOBS[i].title}</h3>
                    <p >${JOBS[i].company}, ${JOBS[i].location}</p>
                    <p>${JOBS[i].date}</p>
                  </div>`;
        
        const jobContent_wide = `
                  <div >
                  <p>${JOBS[i].summary}</p>`;
        
// Here's where I'm stuck. 
// I managed to get the rest of the job details, shown above, displaying correctly.
// I want to show the extra details in an unordered list but I was unable to access
// any individual items in the details array

// I could only show the full array itself but
// manipulating the array itself seems to be impossible
// I keep getting the following error
// script.js:49 Uncaught TypeError: Cannot read properties of undefined (reading 'length') at script.js:49:42

        const jobDetails = document.createElement("ul");
          for (let e=0;e<JOBS[i].details.length;e  ) {
            let listItem = document.createElement("li");
            let item = `<li>${JOBS[i].details[e]}</li>`;
            jobDetails.append(item);
          }
        
                const jobDetails_end = `
                </div>`;
        
                jobEntry.innerHTML = jobContent_narrow   jobContent_wide   jobDetails_end;
                jobSection.append(jobEntry);
              }
        
            myCareer.append(jobSection);

In case you missed the comments, I'm trying to add items from the details array contained in each job as bullet points in an unordered list but have been unable to access them directly. Instead I could display the full array itself.

Any help and advice would be much appreciated and I'll provide any further information if needed.

Thanks.

CodePudding user response:

I see your function initializes the details as this.details, so it could potentially be initialized as undefined if you don't pass that parameter.

Are you sure every single job has details? even as empty array lists?

The error thrown tells you he's trying to get the length of details of every job in the first loop, but got undefined, and calling undefined.length throws that exception

CodePudding user response:

I believe that the problem is caused by the following line on database_career.js:

  ),
  new Job() // <---
];

where you instantiate a new job with details set to undefined. Therefore the following line:

for (let e=0;e<JOBS[i].details.length;e  ) 

is not safe to call because .length does not exist on undefined fields.


What I would recommend is to change this function to something like:

function Job({ title, company, location, date, summary, details = [] } = {}) {
// ...

This uses the destructuring assignment and default parameters (check the docs to learn more about them and see which browsers are supported) and would allow to have something like:

const JOBS = [
  new Job({
    title: "Job title",
    company: "Job company",
    // ...
    details: ["1", "2"]
  }),
  new Job(),
];

where job.details will always be initialised to an empty array and therefore will be safe to loop without explicitly checking for undefined first.

Another thing that I would recommend is to refactor js/script.js and create some small reusable functions, e.g.

function render() {
  // ..
  for (let i=0; i< JOBS.length;i   ) {
    renderJob(JOBS[i]);
  }
}

function renderJob(job) {
  // ...
  renderJobDetails(job.details);
}

function renderJobDetails(details) {
 // ...
}

render();

to improve readability.

If you get stuck again you might want to have a look at this article:

https://javascript.info/debugging-chrome

to learn how to use the browser debugger tool and better understand why your code does not work as expected.

CodePudding user response:

You can nest the template literals

<ul>${job.details.map(detail => `<li>${detail}</li>`).join("")}</ul>

like this

document.querySelector("#work").innerHTML = `
  <div >
    <h2><i  aria-hidden="true"></i>Work Experience</h2>
  ${JOBS.map(job => `
    <div >
      <div >
        <h3>${job.title}</h3>
        <p >${job.company}, ${job.location}</p>
        <p>${job.date}</p>
      </div><div >
        <p>${job.summary}</p>
        <ul>${job.details.map(detail => `<li>${detail}</li>`).join("")}</ul>
      </div>
    </div>`).join("")}
  </div>`
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" integrity="sha512-KfkfwYDsLkIlwQp6LFnl8zNdLGxu9YAA1QvwINks4PhcElQSvqcyVLLD9aMhXd13uQjoXtEKNosOWaZqXgel0g==" crossorigin="anonymous" referrerpolicy="no-referrer" />


<div id="work"></div>

<script>
function Job(title, company, location, date, summary, details) {
  this.title = title;
  this.company = company;
  this.location = location;
  this.date = date;
  this.summary = summary;
  this.details = details;
}


const JOBS = [
  new Job(
    "Job Title",
    "Company",
    "Location",
    "Month 20XX ~ Now",
    "Insert Job Summary Here", ["Bullet Point for notable task or accomplishment 1",
      "Bullet Point for notable task or accomplishment 2",
      "Bullet Point for notable task or accomplishment 3"
    ]
  ),
  new Job(
    "Job Title",
    "Company",
    "Location",
    "Month 20XX ~ Now",
    "Insert Job Summary Here", ["Bullet Point for notable task or accomplishment 1",
      "Bullet Point for notable task or accomplishment 2",
      "Bullet Point for notable task or accomplishment 3"
    ]
  ),

];
</script>

  • Related