Когда мы работаем с API, очень часто мы можем столкнуться с некоторыми API, в которых отсутствуют некоторые данные. И рендеринг каждой информации на внешнем интерфейсе становится непростой задачей. Есть несколько способов визуализации таких данных (неполных данных), но один из наиболее эффективных способов — «Необязательное связывание».

В этой статье мы увидим, насколько крутой является опциональная цепочка. Итак, давайте начнем с этого.

Чтобы показать необязательную цепочку в действии, нам понадобится начальная настройка кода. (HTML, CSS, JavaScript)

HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <!-- styles link -->
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <h2>optional chaining</h2>
    <br /><br />
    <br /><br />
    <div class="container"></div>

    <!-- script -->
    <script src="script.js"></script>
  </body>
</html>

Здесь, в HTML-коде, у нас есть пустой элемент div с классом «container». Мы будем добавлять наши данные в этот div с помощью JavaScript.

CSS. Просто чтобы он выглядел немного лучше (необязательно).

* {
  margin: 0;
  padding: 0;
}

body {
  height: 100vh;
  width: 100vw;
  display: flex;
  align-items: center;
  flex-direction: column;
}

.container {
  font-size: 1.2rem;
  max-width: 500px;
  width: 500px;
}

.card-container {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem;
  margin: 2.5rem;
  box-shadow: 3px 3px 5px #888888;
  border-radius: 5px;
  cursor: pointer;
}

.card-container:hover {
  box-shadow: 5px 5px 8px #5f5f5f;
  transition: all ease 0.5s;
  transform: scale(1.1);
}

h2 {
  text-transform: capitalize;
  font-family: "Courier New", Courier, monospace;
  font-size: 2rem;
  text-decoration: underline;
  margin-top: 20px;
}

.span-container {
  display: flex;
  align-items: center;
}

p {
  color: gray;
}

img {
  width: 150px;
  height: 150px;
  border-radius: 50%;
}

Теперь наступает важная часть (код JavaScript), в которой мы будем отображать данные.

Для этой демонстрации мы будем использовать массив данных. (он локальный, не исходит из какого-либо API)

ДАННЫЕ

const data = [
  {
    id: 1,
    name: "Avinash",
    username: "p4avinash",
    image: [
      {
        profile: {
          url: "https://avatars.githubusercontent.com/u/46785501?v=4",
        },
      },
    ],
  },
  {
    id: 2,
    name: "Ayush",
    username: "f4ayush",
    image: [
      {
        profile: {
          url: "https://avatars.githubusercontent.com/u/41805354?v=4",
        },
      },
    ],
  },
  {
    id: 3,
    name: "Raushan",
    image: [{}],
  },
]

Глядя на эти данные, мы знаем, что перед нами массив и в объектах присутствует другая информация. Мы можем легко сопоставить этот массив и создать динамический HTML-код в JavaScript. (В этой статье я буду немного схитрить: вместо использования createElement я буду использовать литералы шаблонов, просто чтобы немного ускорить работу.)

Но загвоздка с этими данными заключается в свойстве изображения. Давайте посмотрим это на коде:

JavaScript

const container = document.querySelector(".container")
let element = ``

data.map((item) => {
  const { id, name, username, image } = item

  console.log(image)
  let img =
    image[0].profile.url ||
    `https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_1280.png`

  element += `<div class="card-container">
        <img
          src=${img}
          alt=${name}
        />
        <div class="details">
          <span class="span-container">
            ID:
            <p>${id}</p>
          </span>
          <span class="span-container">
            Name:
            <p>${name}</p>
          </span>
          <span class="span-container">
            Username:
            <p>${username || "unavailable"}</p>
          </span>
        </div>
      </div>`

  container.innerHTML = element
})

В этом коде

  1. Мы выбрали контейнер из HTML.

2. Сопоставление данных.

3. Добавлен HTML к «элементу».

4. Добавлен последний «элемент» к «контейнеру».

Теперь посмотрим, какой результат мы получим:

Хм..!! Что-то здесь не так, у нас есть 3 объекта в нашем массиве данных, но отрисовано только 2. И у нас возникает ошибка, говорящая, что он не может найти какой-то URL. Поскольку мы знаем, что в данных присутствуют только URL-адреса изображений, а еще два уже отображаются на экране, это должен быть URL-адрес последнего пользователя Раушана, который отсутствует. И мы не смогли это отобразить.

Чтобы понять это, нам нужно сосредоточиться на этой части:

  const { id, name, username, image } = item

  console.log(image)
  let img =
    image[0].profile.url ||
    `https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_1280.png`

Мы деструктурировали элемент, чтобы получить свойства: id, имя, имя пользователя и изображение. Мы также могли бы использовать в динамическом html-коде item.id, item.name и т. д. Но мы деструктурировали его, чтобы сделать его немного быстрее.

Теперь URL-адрес изображения был вложенным, поэтому нам нужно было добраться до этого URL-адреса, чтобы его отобразить, и это то, что мы делаем с кодом «image[0].profile.url».

Затем мы просто присвоили это переменной «img» и использовали эту переменную в динамическом HTML для визуализации изображения.

Но если вы обратите пристальное внимание, мы используем другое изображение в качестве заполнителя, поэтому технически, если исходный URL-адрес изображения отсутствует, это должно отобразить изображение-заполнитель, верно?

Ответ: в данном случае этого не должно быть. Потому что изображение-заполнитель будет отображаться только тогда, когда оно сможет найти профиль изображения › [отсутствует URL-адрес]. Здесь, чтобы отобразить изображение-заполнитель, оно также должно иметь свойство профиля.

Как мы можем это исправить?

Я хочу сказать, что мы всегда можем увидеть и узнать, какова структура данных. Но мы никогда не можем быть уверены в том, какое свойство данных будет отсутствовать.

Поэтому нам нужен способ, с помощью которого мы могли бы эффективно проверять каждое свойство данных, и если оно отсутствует, мы отображаем значение по умолчанию. В противном случае мы столкнулись бы с той же ошибкой, что мы видели выше.

И чтобы сделать это возможным, необходимо добавить дополнительную цепочку.

Мы можем изменить код как таковой:

//old
  let img =
    image[0].profile.url ||
    `https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_1280.png`

//correct way (optional chaining)
  let img =
    (image[0] && image[0].profile && image[0].profile.url) ||
    `https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_1280.png`

Этот код просто говорит:

if(image[0]){
  //check for profile
  if(image[0].profile){
    //check for url
    if(url){
      //render the url
    } else{
      //render the placeholder image
    }
  } else{
    //render the placeholder image
  }
} else{
  //render the placeholder image
}

Результат:

Это намного лучше и круче, не так ли? Мы можем сделать это в одной строке кода.

Хотя, если мы захотим, мы можем сделать этот однострочный код даже меньшим количеством символов:

Альтернативный синтаксис для необязательного связывания:

//optional chaining
let img =
    (image[0] && image[0].profile && image[0].profile.url) ||
    `https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_1280.png`


//alternate syntax for optional chaining
let img =
    image[0]?.profile?.url ||
    `https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_1280.png`

Итак, это необязательная цепочка, которая сэкономит ваше время и поможет писать более эффективный код.