Home > Mobile >  Error in fetching product details from API
Error in fetching product details from API

Time:10-26

I have been trying to figure out the reason behind what could be wrong exactly.

The basic HTML and CSS have been pre-written, but JavaScript is used to create the core elements (or items) meant to populate the DOM.

In my script, I am trying to fetch product information to populate the browser interface with shopping items.

The error that I am getting in the console is: Cannot set properties of undefined (setting 'innerText')

async function fakeStoreAPI_Products () {
    const urlOfData = await fetch('https://fakestoreapi.com/products');
    if(urlOfData.status !== 200 || !urlOfData.ok){
        throw new Error('There was a problem fetching the data...')
    }else if(urlOfData.status === 200 && urlOfData.ok){
        const response = await urlOfData.json();
        populate(response)
    }
}

function populate (completeData) {
    const displayAllCardsHere = document.getElementById('cards_inner');    
    const data = completeData;
    
    for(const datum of data){
    // The parent
    const div_class_card = document.createElement('div')
    div_class_card.setAttribute('class', 'card')
    // Child [p_class_title]
    const p_class_title = document.createElement('p')
    p_class_title.setAttribute('class', 'title')
    // Child [img]
    const img = document.createElement('div')
    img.setAttribute('class', 'img')
    // Child [p_class_description]
    const p_class_description = document.createElement('p')
    p_class_description.setAttribute('class', 'description')
    // Child [div_class_cat_price] ~ With children
    const div_class_cat_price = document.createElement('div')
    div_class_cat_price.setAttribute('class', 'cat_price')
    // Children of [div_class_cat_price] ~ descendants of [div_class_card ~ The [grand] parent]
    const p_class_category = document.createElement('p')
    p_class_category.setAttribute('class', 'category')
    const p_class_price = document.createElement('p')
    p_class_price.setAttribute('class', 'price')
    
    p_class_title.innerText = datum['title']
    img.setAttribute('src', 'datum["image"]')
    p_class_description.innerText = datum['description']
    p_class_category.innerText = datum['category']
    p_class_price.innerText = datum['price']
    
    //div_class_card.insertAdjacentHTML('beforeend', p_class_title)
    div_class_card.append(p_class_title)
    //div_class_card.insertAdjacentHTML('beforeend', img)
    div_class_card.append(img)
    //div_class_card.insertAdjacentHTML('beforeend', p_class_description)
    div_class_card.append(p_class_description)
    
    //div_class_card.insertAdjacentHTML('beforeend', div_class_cat_price)
    div_class_card.append(div_class_cat_price)
    
    // Place children [p_class_category, p_class_price] into parent [div_class_cat_price]
    //div_class_cat_price.insertAdjacentHTML('beforeend', p_class_category)
    div_class_cat_price.append(p_class_category)
    //div_class_cat_price.insertAdjacentHTML('beforeend', p_class_price)
    div_class_cat_price.append(p_class_price)
    
    displayAllCardsHere.append(div_class_card)
}
}

fakeStoreAPI_Products().catch((err) => {
    console.log(err)
})
*{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body{
    font-family: Nunito Sans, Yu Gothic UI;
}

div#cards_outer{
    width: 100%;
    padding: 20px;
    display: grid;
    grid-row-gap: 20px;
}

div#heading{
    text-align: center;
}

div#heading span{
    font-size: 40px;
    font-weight: 600;
    /* hori verti blur colour */
    text-shadow: 0px 0px 6px #0007;
}

div#cards_inner{
    width: 100%;
    display: grid;
    grid-template: 1fr / repeat(auto-fit, minmax(200px, 1fr));
    grid-gap: 20px;
}

div#cards_inner div.card{
    /* hori verti blur color */
    box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);
    padding: 10px;
}

div#cards_inner div.card img{
    width: 100%;
}

div#cards_inner div.card p.description{
    font-size: 14px;
    text-align: justify;
    word-wrap: break-word;
}

div#cards_inner div.card p.title,
div#cards_inner div.card div.cat_price p.category,
div#cards_inner div.card div.cat_price p.price{
    text-align: center;
}

div#cards_inner div.card p.title{
    font-size: 18px;
    font-weight: 600;
    text-transform: capitalize;
}

div#cards_inner div.card div.cat_price p.category,
div#cards_inner div.card div.cat_price p.price{
    font-size: 14px;
    font-weight: 600;
}

div#cards_inner div.card div.cat_price p.category{
    text-transform: capitalize;
}
<!DOCTYPE html>
<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" />
    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Nunito Sans&display=swap" />
    <title>Fetch data from API and display data on the browser</title>
</head>

<body>
    <div id="cards_outer">
        <div id="heading"><span>Fake API Store</span></div>
        <div id="cards_inner">
            <!-- populate cards here -->
        </div>
    </div>
</body>

</html>

What exactly did I miss?

CodePudding user response:

The problem is, that you set the variable to document.createElement('div').setAttribute('class', 'title'), but setAttribute('class', 'title') doesn't return the created HTML element.

You should do it like this:

// The parent
const div_class_card = document.createElement('div')
div_class_card.setAttribute('class', 'card')
// Child [p_class_title]
const p_class_title = document.createElement('p')
p_class_title.setAttribute('class', 'title')
// Child [img]
const img = document.createElement('img')
img.setAttribute('class', 'img')
// Child [p_class_description]
const p_class_description = document.createElement('p')
p_class_description.setAttribute('class', 'description')
// Child [div_class_cat_price] ~ With children
const div_class_cat_price = document.createElement('div')
div_class_cat_price.setAttribute('class', 'cat_price')
// Children of [div_class_cat_price] ~ decendants of [div_class_card ~ The [grand] parent]
const p_class_category = document.createElement('p')
p_class_category.setAttribute('class', 'category')
const p_class_price = document.createElement('p')
p_class_price.setAttribute('class', 'price')

First create the element and set the variable for it, then use the variable to set attributes.

Edit:

The image is not shown, because you create a div element in your img variable. And div elements don't handle src attributes. It should work, if you do the following:

// Child [img]
const img = document.createElement('img')
img.setAttribute('class', 'img')

Also, you are using the string 'datum["image"]' as the src attribute. It should be the following.

img.setAttribute('src', datum["image"])

CodePudding user response:

My preference is use HTML for HTML and then use javascript to manipulate/populate the HTML. From your code, the internal structure of your card is not immediately obvious. Use the <template> tag to structure your card. Then use javascript to populate a clone of the template, then add that to the DOM.

Not only do I find it easier to work with HTML as HTML, this also results in simpler javascript.

/*Nothing has changed here, move along*/
async function fakeStoreAPI_Products() {
  const urlOfData = await fetch('https://fakestoreapi.com/products');
  if (urlOfData.status !== 200 || !urlOfData.ok) {
    throw new Error('There was a problem fetching the data...')
  } else if (urlOfData.status === 200 && urlOfData.ok) {
    const response = await urlOfData.json();
    populate(response)
  }
}

function populate(completeData) {
  const displayAllCardsHere = document.getElementById('cards_inner');
  //Get the template object
  const template = document.getElementById("cardTemplate");  
  const data = completeData;  
  
  for (const datum of data) {
     //Clone the template
    let clone = template.content.cloneNode(true);
    /*Use query selectors to populate the clone*/
    clone.querySelector(".title").innerText = datum["title"];
    clone.querySelector(".img img").setAttribute("src", datum["image"]);
    clone.querySelector(".description").innerText = datum["description"];
    clone.querySelector(".cat_price .category").innerText = datum["category"];
    clone.querySelector(".cat_price .price").innerText = datum["price"];
    
    //Append clone to the DOM
    displayAllCardsHere.appendChild(clone);
  }
}

fakeStoreAPI_Products().catch((err) => {
  console.log(err)
})
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: Nunito Sans, Yu Gothic UI;
}

div#cards_outer {
  width: 100%;
  padding: 20px;
  display: grid;
  grid-row-gap: 20px;
}

div#heading {
  text-align: center;
}

div#heading span {
  font-size: 40px;
  font-weight: 600;
  /* hori verti blur colour */
  text-shadow: 0px 0px 6px #0007;
}

div#cards_inner {
  width: 100%;
  display: grid;
  grid-template: 1fr / repeat(auto-fit, minmax(200px, 1fr));
  grid-gap: 20px;
}

div#cards_inner div.card {
  /* hori verti blur color */
  box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);
  padding: 10px;
}

div#cards_inner div.card img {
  width: 100%;
}

div#cards_inner div.card p.description {
  font-size: 14px;
  text-align: justify;
  word-wrap: break-word;
}

div#cards_inner div.card p.title,
div#cards_inner div.card div.cat_price p.category,
div#cards_inner div.card div.cat_price p.price {
  text-align: center;
}

div#cards_inner div.card p.title {
  font-size: 18px;
  font-weight: 600;
  text-transform: capitalize;
}

div#cards_inner div.card div.cat_price p.category,
div#cards_inner div.card div.cat_price p.price {
  font-size: 14px;
  font-weight: 600;
}

div#cards_inner div.card div.cat_price p.category {
  text-transform: capitalize;
}
<div id="cards_outer">
  <div id="heading"><span>Fake API Store</span></div>
  <div id="cards_inner">
    <!-- populate cards here -->
  </div>
</div>
<template id="cardTemplate">
  <div >
      <p ></p>
      <div ><img src=""/></div>
      <div ></div>
      <div >
        <p ></p>
        <p ></p>        
      </div>
  </div>
</template>

  • Related