pobieranie danych z api

0

Chcę utowrzyć zagnieżdzone ul, jak to zrobić nie znając struktury json? Jak np. pozyskać np. nazwę candidate? np. data[i].candidate - jak to zapisać nie wiedząc, że tam jest ta nazwa?
Wkleiłem kawałek kodu i plik json.

fetch('./data.json')
            .then(function (response) {
                return response.json();
            })
            .then(function (data) {
                
                let candidates = document.querySelector(".candidates");
                for (let i = 0; i < data.length; i++) {
                    let li = document.createElement('li');
                    candidates.appendChild(li);
                }
            })
            .catch(function (err) {
                console.log(err)
            });
[
    {
        "candidate": {
            "first_name": "Margaret",
            "skills": [
                "skLearn",
                "Java",
                "R",
                "SQL",
                "Spark",
                "C++"
            ],
            "interests": [
                "swimmigng",
                "climbing"
            ]
        }
    },
    {
        "candidate": {
            "first_name": "Olivia",
            "skills": [
                "Spark",
                "C++"
            ],
            "interests": [
                "golf",
                "skiing"
            ]
        }
    }
]
3

Jest kilka sposobów, żeby to odczytać

  1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
  2. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
  3. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
const json = {
  "first_name": "Margaret",
  "age": 32,
  "city": "Warsaw",
  "country": "Poland",
}

// 1
for (const [name, value] of Object.entries(json)) {
  console.log(`Pole o nazwie "${name}" ma wartość "${value}"`);
}

// 2
for (const name of Object.keys(json)) {
  console.log(`Pole o nazwie "${name}" ma wartość "${json[name]}"`);
}

// 3
for (const name in json) {
   console.log(`Pole o nazwie "${name}" ma wartość "${json[name]}"`);
}
0

A jeśli mam zagnieżdżony obiekt to definiować potem wartości jako klucze czy jak?

0
mistrzkrisu6 napisał(a):

A jeśli mam zagnieżdżony obiekt to definiować potem wartości jako klucze czy jak?

To zależy co chcesz potem z tym zrobić.

JavaScript nie udostępnia gotowej funkcji do iteracji po zagnieżdżonych obiektach, więc musiałbyś poszukać biblioteki na npm, albo zrobić własną prostą implementacje.

Oto mój przykład z wykorzystaniem rekurencji

const treeLoop = (value, callback) => {
  if (Array.isArray(value) || (typeof value === 'object' && value)) {
    for (const key in value) {
      callback(key, value[key]);
      treeLoop(value[key], callback);
    }
  }
}

treeLoop(json, (key, value) => {
  console.log('------');
  console.log(`${key} <--> ${JSON.stringify(value)}`);
});

Funkcja przechodzi przez całą strukturę JSON'a i przy każdej wartości wywołuje ten sam callback.

0

musze stworzyć zagnieżdżona listę mając plik json

1
Xarviel napisał(a):
mistrzkrisu6 napisał(a):

A jeśli mam zagnieżdżony obiekt to definiować potem wartości jako klucze czy jak?

To zależy co chcesz potem z tym zrobić.

JavaScript nie udostępnia gotowej funkcji do iteracji po zagnieżdżonych obiektach, więc musiałbyś poszukać biblioteki na npm, albo zrobić własną prostą implementacje.

Oto mój przykład z wykorzystaniem rekurencji

const treeLoop = (value, callback) => {
  if (Array.isArray(value) || (typeof value === 'object' && value)) {
    for (const key in value) {
      callback(key, value[key]);
      treeLoop(value[key], callback);
    }
  }
}

treeLoop(json, (key, value) => {
  console.log('------');
  console.log(`${key} <--> ${JSON.stringify(value)}`);
});

Funkcja przechodzi przez całą strukturę JSON'a i przy każdej wartości wywołuje ten sam callback.

zrobiłem tak - link do strony link i kodu link jednak nie wiem jak to zgrać by działało poprawnie

1
// Twój plik data.json
const json =  {
  Candidates: {
    Margaret: {
      skills: ['skLearn', 'Java', 'R', 'SQL', 'Spark', 'C++'],
      interests: ['swimmigng', 'climbing'],
    },
    Olivia: {
      skills: ['Spark', 'C++'],
      interests: ['golf', 'skiing'],
    },
  },
};

// Funkcja, która przechodzi przez cały JSON i uruchamia podany callback
const treeLoop = (value, callback, index = 0) => {
  if (Array.isArray(value) || typeof value === 'object') {
    for (const key in value) {
      callback(key, value[key], index);
      treeLoop(value[key], callback, index + 1);
    }
  }
};

// Tworzy element <ul>
// Parametr index jest jedynie po to, żeby łatwo można byłoby odnaleźć element przez querySelectorAll
const createList = (index) => {
  const list = document.createElement('ul');
  list.dataset.index = index;

  return list;
}

// Tworzy element <li>
// Parametr key dodałem, żeby ułatwić wyszukiwanie przez querySelector
const createItem = (key, value) => {
  const listItem = document.createElement('li');
  listItem.dataset.key = key;
  listItem.textContent = !/^\d$/.test(key) || typeof value === 'object' ? key : value;

  return listItem;
}

// Pobiera ostatni znacznik <ul> z danym atrybutem data-index
const getList = (index) => {
  if (index === 0) {
    return rootList;
  }

  const list = rootList.querySelectorAll(`:scope ul[data-index="${index}"]`);
  return list[list.length - 1];
}

// Pobiera znacznik <li> z danej listy
const getItem = (lastList, key) => {
  return lastList.querySelector(`:scope > li[data-key="${key}"]`);;
}

// Główna lista
const rootList = createList(0);

// Tutaj cały mechanizm i wykorzystanie wszystkich funkcji pomocniczych
treeLoop(json, (key, value, index) => {
  const list = getList(index) || createList(index);
  const item = getItem(list, key) || createItem(key, value);

  if (typeof value === "object") {
    item.appendChild(createList(index + 1));
  }

  list.appendChild(item);
});

// Wyświetlenie całej listy na stronie
document.body.append(rootList);

I wynikiem tego wszystkiego jest takie drzewko zbudowane z JSON'a.

  • Candidates
    • Margaret
      • skills
        • skLearn
        • Java
        • R
        • SQL
        • Spark
        • C++
      • interests
        • swimmigng
        • climbing
    • Olivia
      • skills
        • Spark
        • C++
      • interests
        • golf
        • skiing
0
Xarviel napisał(a):
// Twój plik data.json
const json =  {
  Candidates: {
    Margaret: {
      skills: ['skLearn', 'Java', 'R', 'SQL', 'Spark', 'C++'],
      interests: ['swimmigng', 'climbing'],
    },
    Olivia: {
      skills: ['Spark', 'C++'],
      interests: ['golf', 'skiing'],
    },
  },
};

// Funkcja, która przechodzi przez cały JSON i uruchamia podany callback
const treeLoop = (value, callback, index = 0) => {
  if (Array.isArray(value) || typeof value === 'object') {
    for (const key in value) {
      callback(key, value[key], index);
      treeLoop(value[key], callback, index + 1);
    }
  }
};

// Tworzy element <ul>
// Parametr index jest jedynie po to, żeby łatwo można byłoby odnaleźć element przez querySelectorAll
const createList = (index) => {
  const list = document.createElement('ul');
  list.dataset.index = index;

  return list;
}

// Tworzy element <li>
// Parametr key dodałem, żeby ułatwić wyszukiwanie przez querySelector
const createItem = (key, value) => {
  const listItem = document.createElement('li');
  listItem.dataset.key = key;
  listItem.textContent = !/^\d$/.test(key) || typeof value === 'object' ? key : value;

  return listItem;
}

// Pobiera ostatni znacznik <ul> z danym atrybutem data-index
const getList = (index) => {
  if (index === 0) {
    return rootList;
  }

  const list = rootList.querySelectorAll(`:scope ul[data-index="${index}"]`);
  return list[list.length - 1];
}

// Pobiera znacznik <li> z danej listy
const getItem = (lastList, key) => {
  return lastList.querySelector(`:scope > li[data-key="${key}"]`);;
}

// Główna lista
const rootList = createList(0);

// Tutaj cały mechanizm i wykorzystanie wszystkich funkcji pomocniczych
treeLoop(json, (key, value, index) => {
  const list = getList(index) || createList(index);
  const item = getItem(list, key) || createItem(key, value);

  if (typeof value === "object") {
    item.appendChild(createList(index + 1));
  }

  list.appendChild(item);
});

// Wyświetlenie całej listy na stronie
document.body.append(rootList);

I wynikiem tego wszystkiego jest takie drzewko zbudowane z JSON'a.

  • Candidates
    • Margaret
      • skills
        • skLearn
        • Java
        • R
        • SQL
        • Spark
        • C++
      • interests
        • swimmigng
        • climbing
    • Olivia
      • skills
        • Spark
        • C++
      • interests
        • golf
        • skiing

dzięki, ciężko mi się trochę w tym połapać, chciałbym zrobić by te ul były zwijane i wcześniej robiłem to w tagu

<details>
  <summary>Szczypta detali</summary>
  <p>Informacje szczegółowe na dany temat.</p>
</details>

chciałem kod tworzenia listy zmienić na

const createList = (index) => {
    const details = document.createElement('details');
    details.dataset.index = index;
    const summary = document.createElement('summary');
    details.appendChild(summary);
    const list = document.createElement('ul');

  return details;
}

ale jednak to jest źle

1

@mistrzkrisu6 tworzysz znacznik details, dodajesz tam znacznik summary, ale nie dodajesz listy z samego końca.

Dodałem tutaj jedynie jeden warunek + firstElementChild, ale wstawiam całe dwie funkcje, żebyś miał łatwiej z dodaniem u siebie zmian.

const createItem = (key, value) => {
  const listItem = document.createElement('li');
  listItem.dataset.key = key;

  if (typeof value === "object") { // <--- ten warunek jest nowy
    const details = document.createElement('details');
    const summary = document.createElement('summary');
    summary.textContent = key;

    details.append(summary);
    listItem.append(details)
  } else {
    listItem.textContent = !/^\d$/.test(key) ? key : value;
  }

  return listItem;
}

treeLoop(json, (key, value, index) => {
  const list = getList(index) || createList(index);
  const item = getItem(list, key) || createItem(key, value);

  if (typeof value === "object") {
    item.firstElementChild.appendChild(createList(index + 1)); // <-- dochodzi nam tutaj firstElementChild, żeby wstawiać <ul> do znacznika <details>
  }

  list.appendChild(item);
});

screenshot-20220503175434.png

0
Xarviel napisał(a):

@mistrzkrisu6 tworzysz znacznik details, dodajesz tam znacznik summary, ale nie dodajesz listy z samego końca.

Dodałem tutaj jedynie jeden warunek + firstElementChild, ale wstawiam całe dwie funkcje, żebyś miał łatwiej z dodaniem u siebie zmian.

const createItem = (key, value) => {
  const listItem = document.createElement('li');
  listItem.dataset.key = key;

  if (typeof value === "object") { // <--- ten warunek jest nowy
    const details = document.createElement('details');
    const summary = document.createElement('summary');
    summary.textContent = key;

    details.append(summary);
    listItem.append(details)
  } else {
    listItem.textContent = !/^\d$/.test(key) ? key : value;
  }

  return listItem;
}

treeLoop(json, (key, value, index) => {
  const list = getList(index) || createList(index);
  const item = getItem(list, key) || createItem(key, value);

  if (typeof value === "object") {
    item.firstElementChild.appendChild(createList(index + 1)); // <-- dochodzi nam tutaj firstElementChild, żeby wstawiać <ul> do znacznika <details>
  }

  list.appendChild(item);
});

screenshot-20220503175434.png

chciałbym zrobić by np. tam gdzie ul mają indexy parzyste to jak klikam na inny details co ma ul z tym indexem to by nie dało się go otworzyć i myślałem o czymś takim

document.querySelectorAll('details').forEach(item => {
  item.addEventListener('click' , e => {
    if (item.dataset.index % 2 != 0) {
    warunek do sprawdzenia czy istnieje już details o tym id który jest otwarty
      item.open = false;
    }
  })
})
    

tylko nie wiem jak zrobić by details też miało index np. taki jak ul i właśnie warunek który napisałem

0
mistrzkrisu6 napisał(a):

chciałbym zrobić by np. tam gdzie ul mają indexy parzyste to jak klikam na inny details co ma ul z tym indexem to by nie dało się go otworzyć i myślałem o czymś takim

document.querySelectorAll('details').forEach(item => {
  item.addEventListener('click' , e => {
    if (item.dataset.index % 2 != 0) {
    warunek do sprawdzenia czy istnieje już details o tym id który jest otwarty
      item.open = false;
    }
  })
})
    

tylko nie wiem jak zrobić by details też miało index np. taki jak ul i właśnie warunek który napisałem

NIe musimy przypisywać tego bezpośrednio do details, bo możemy odwołać się do ul przykładowo za pomocą metody closest.

for (const list of document.querySelectorAll('ul[data-index]')) { // Pobieramy wszystkie listy
  const index = Number(list.dataset.index); // Zamieniamy atrybut data-index w liczbę (domyślnie jest stringiem)
  const details = list.closest('details'); // Wyszukujemy najbliższy znacznik details

  if (!details) { // Sprawdzamy, czy details nie istnieje
    continue; // Jeśli tak to resetujemy pętle
  }

  details.addEventListener('click', (e) => { // Podpinamy event click
    e.preventDefault(); // Resetujemy domyślny event
    e.stopPropagation(); // Wyłączamy propagacje

    if (index % 2 !== 0 || e.currentTarget.open) { // Jeśli index jest parzysty lub details jest otwarte
      e.currentTarget.open = !e.currentTarget.open; // To zamykamy go 

      return; // I wychodzimy z eventu
    }

    for (const indexList of document.querySelectorAll(`ul[data-index="${index}"]`)) { // Robimy pętle po pobranych listach
      const indexDetails = indexList.closest('details'); // Wyszukujemy najbliższy znacznik details
      
      if (indexDetails.open) { // Sprawdzamy, czy jest otwarty
        return; // Jeśli tek to przerywamy event
      }
    }

    e.currentTarget.open = true; // Na samym końcu jeśli wszystkie warunki nie zadziałały to rozwijamy details
  });
}
0
Xarviel napisał(a):
mistrzkrisu6 napisał(a):

chciałbym zrobić by np. tam gdzie ul mają indexy parzyste to jak klikam na inny details co ma ul z tym indexem to by nie dało się go otworzyć i myślałem o czymś takim

document.querySelectorAll('details').forEach(item => {
  item.addEventListener('click' , e => {
    if (item.dataset.index % 2 != 0) {
    warunek do sprawdzenia czy istnieje już details o tym id który jest otwarty
      item.open = false;
    }
  })
})
    

tylko nie wiem jak zrobić by details też miało index np. taki jak ul i właśnie warunek który napisałem

NIe musimy przypisywać tego bezpośrednio do details, bo możemy odwołać się do ul przykładowo za pomocą metody closest.

for (const list of document.querySelectorAll('ul[data-index]')) { // Pobieramy wszystkie listy
  const index = Number(list.dataset.index); // Zamieniamy atrybut data-index w liczbę (domyślnie jest stringiem)
  const details = list.closest('details'); // Wyszukujemy najbliższy znacznik details

  if (!details) { // Sprawdzamy, czy details nie istnieje
    continue; // Jeśli tak to resetujemy pętle
  }

  details.addEventListener('click', (e) => { // Podpinamy event click
    e.preventDefault(); // Resetujemy domyślny event
    e.stopPropagation(); // Wyłączamy propagacje

    if (index % 2 !== 0 || e.currentTarget.open) { // Jeśli index jest parzysty lub details jest otwarte
      e.currentTarget.open = !e.currentTarget.open; // To zamykamy go 

      return; // I wychodzimy z eventu
    }

    for (const indexList of document.querySelectorAll(`ul[data-index="${index}"]`)) { // Robimy pętle po pobranych listach
      const indexDetails = indexList.closest('details'); // Wyszukujemy najbliższy znacznik details
      
      if (indexDetails.open) { // Sprawdzamy, czy jest otwarty
        return; // Jeśli tek to przerywamy event
      }
    }

    e.currentTarget.open = true; // Na samym końcu jeśli wszystkie warunki nie zadziałały to rozwijamy details
  });
}

chciałbym jeszcze by kiedy rozwijam ostatnią listę w gałęzi by nie mieć tego paska szarego do rozwijania, jak mogę sprawdzić ten warunek bo nawet jeśli to jest ostatnie ul z tym indexem to te indexy powtarzają się w innej gałęzi - tu jest kod
link a tu strona link

1

O to już pytałeś w innym temacie i nawet jest tam moja odpowiedź :] (linie konturowe w zagnieżdżonym ul)

Spróbowałbym w taki sposób, że na ostatnie <li> z listy nakładałbym pseudo element ::before/::after z position: absolute, który by to zakrywał.

0
Xarviel napisał(a):

O to już pytałeś w innym temacie i nawet jest tam moja odpowiedź :] (linie konturowe w zagnieżdżonym ul)

Spróbowałbym w taki sposób, że na ostatnie <li> z listy nakładałbym pseudo element ::before/::after z position: absolute, który by to zakrywał.

tylko właśnie ten border-left jest na ul, mam jeszcze problem z płynnym zamykaniem details bo chciałbym zrobić styl równocześnie do każdego a każdy jest innej wysokości i nie mogę na sztywno wpisać wartości, czy da się to jakoś bez zmieniania height zrobić? Tak jak mam tu to przy zamykaniu od razu się zwija

details[open] summary ~ ul {
  animation: sweep .5s ease-in-out;
}
details[close] summary ~ ul {
  animation: sweepo .5s ease-in-out;
}

@keyframes sweep {
  0%    {opacity: 0; margin-left: -10px}
  100%  {opacity: 1; margin-left: 0px}
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1