@Esm322

Какими способами можно получить первое попавшееся изображение?

Здравствуйте. Пожалуйста, помогите с решением задачи. Есть данные с сервера, необходимо отрендерить карточку товара с изображением и др данными. Вывести др данные (id, title и price) получается, а с изображениями появилась проблема. Когда я обхожу все эти данные, то выводятся сразу все изображения по отдельности. А мне нужно, чтобы выводилась карточка с первым попавшимся изображением, тк в дальнейшем карточка будет менять изображение при выборе цвета товара. И если есть возможность, покидать ссылки на открытые репозитории по написанию проектов на vue. Заранее, всем спасибо за ответы. Пример ответа с сервера:
"items": [
    {
      "id": 1,
      "title": "Носки",
      "slug": "noski",
      "price": 199,
      "colors": [
        {
          "id": 44,
          "color": {
            "id": 20,
            "title": "Red",
            "code": "#dd0808"
          },
          "gallery": [
            {
              "file": {
                "url": "ссылка на изображение",
                "name": "324e1f4fb34931a1d367602a730a75b5.jpg",
                "originalName": "Rectangle 121.jpg",
                "extension": "jpg",
                "size": "4.3 Кб"
              }
            }
          ]
        }
      ],
      "seasons": [
        {
          "id": 2,
          "title": "Осень",
          "code": "autumn",
          "productsCount": 10
        },
        {
          "id": 4,
          "title": "Весна",
          "code": "spring",
          "productsCount": 10
        }
      ],
      "materials": [
        {
          "id": 2,
          "title": "Шерсть",
          "code": "wool",
          "productsCount": 4
        }
      ]
    },
    {
      "id": 2,
      "title": "Футболка",
      "slug": "futbolka",
      "price": 1000,
      "colors": [
        {
          "id": 77,
          "color": {
            "id": 27,
            "title": "white",
            "code": "#ffffff"
          },
          "gallery": [
            {
              "file": {
                "url": "ссылка на изображение",
                "name": "36efa79c31cb4400f8f9ce69a7d6f6a6.png",
                "originalName": "Rectangle 13++.png",
                "extension": "png",
                "size": "63.5 Кб"
              }
            }
          ]
        }
      ],
      "seasons": [
        {
          "id": 3,
          "title": "Лето",
          "code": "summer",
          "productsCount": 10
        }
      ],
      "materials": [
        {
          "id": 1,
          "title": "Хлопок",
          "code": "cotton",
          "productsCount": 6
        },
        {
          "id": 2,
          "title": "Шерсть",
          "code": "wool",
          "productsCount": 4
        }
      ]
    },
    {
      "id": 3,
      "title": "Кружевной бюстгалтер без косточек",
      "slug": "kruzhevnoy-byustgalter-bez-kostochek",
      "price": 3690,
      "colors": [
        {
          "id": 45,
          "color": {
            "id": 21,
            "title": "Blue",
            "code": "#180ae6"
          },
          "gallery": [
            {
              "file": {
                "url": "ссылка на изображение",
                "name": "cdbd75ce872ca9ef1860378415f90dda.png",
                "originalName": "Rectangle 1211111.png",
                "extension": "png",
                "size": "110.3 Кб"
              }
            }
          ]
        },
        {
          "id": 76,
          "color": {
            "id": 20,
            "title": "Red",
            "code": "#dd0808"
          },
          "gallery": [
            {
              "file": {
                "url": "ссылка на изображение",
                "name": "064e02cba644eca0cdc2e844e31315cf.png",
                "originalName": "Rectangle 1211111.png",
                "extension": "png",
                "size": "113.7 Кб"
              }
            }
          ]
        }
      ],
      "seasons": [
        {
          "id": 3,
          "title": "Лето",
          "code": "summer",
          "productsCount": 10
        },
        {
          "id": 4,
          "title": "Весна",
          "code": "spring",
          "productsCount": 10
        }
      ],
      "materials": [
        {
          "id": 3,
          "title": "Полиэстер",
          "code": "PES",
          "productsCount": 7
        },
        {
          "id": 4,
          "title": "Полиамид",
          "code": "PA",
          "productsCount": 4
        }
      ]
    }
  ],

Страница MainPage во Vue:
<template>
  <main class="content container">
    <div class="content__top">

      <div class="content__row">
        <h1 class="content__title">
          Каталог
        </h1>
        <span class="content__info">
          152 товара
        </span>
      </div>
    </div>

    <div class="content__catalog">

      <ProductFilter v-model:price-from="filterPriceFrom"
      v-model:price-to="filterPriceTo"
      v-model:category-id="filterCategoryId"
      v-model:material-id="filterMaterialId"
      v-model:season-id="filterSeasonId"/>

      <section class="catalog">

        <button @click.prevent="getSome">asdsad</button>

        <ProductList :products="products"/>

        <BasePagination v-model="page" :count="countProducts" :per-page="productsPerPage"/>

      </section>
    </div>
  </main>
</template>

<script>
import ProductList from '@/components/ProductList.vue';
import ProductFilter from '@/components/ProductFilter.vue';
import BasePagination from '@/components/BasePagination.vue';
import axios from 'axios';
import { API_BASE_URL } from '@/config';
import numberFormat from '@/helpers/numberFormat';

export default {
  data() {
    return {
      productsData: null,

      page: 1,
      productsPerPage: 5,

      filterPriceFrom: 0,
      filterPriceTo: 0,
      filterCategoryId: 0,
      filterSeasonId: [],
      filterMaterialId: [],
      filterColorId: [],
    };
  },
  components: {
    ProductList,
    ProductFilter,
    BasePagination,
  },
  computed: {
    products() {
      return this.productsData ? this.productsData.items.map((item) => {
        return {
          ...item,
          price: numberFormat(item.price),
        };
      }) : [];
    },
    countProducts() {
      return this.productsData ? this.productsData.pagination.total : 0;
    },
  },
  methods: {
    getSome() {
      console.log(this.image);
    },
    loadProducts() {
      return axios
        .get(`${API_BASE_URL}/api/products`, {
          params: {
            page: this.page,
            limit: this.productsPerPage,
            minPrice: this.filterPriceFrom,
            maxPrice: this.filterPriceTo,
            categoryId: this.filterCategoryId,
            materialIds: this.filterMaterialId,
            seasonIds: this.filterSeasonId,
            colorIds: this.filterColorId,
          },
        })
        .then((response) => this.productsData = response.data);
    },
  },
  watch: {
    page() {
      this.loadProducts();
    },
    filterPriceFrom() {
      this.loadProducts();
    },
    filterPriceTo() {
      this.loadProducts();
    },
    filterCategoryId() {
      this.loadProducts();
    },
    filterMaterialId() {
      this.loadProducts();
    },
    filterSeasonId() {
      this.loadProducts();
    },
  },
  created() {
    this.loadProducts();
  },
};
</script>

Компонент ProductItem во Vue:
<template>
  <li class="catalog__item" v-for="product in products" :key="product.id">
    <router-link :to="{name: 'product', params:{id: product.id}}"
    class="catalog__pic" href="#">
      <img src="" :alt="product.title">
    </router-link>

    <h3 class="catalog__title">
      <a href="#">
        {{ product.title }}
      </a>
    </h3>

    <span class="catalog__price">
      {{ product.price }} ₽
    </span>
    <ul class="colors colors--black">
      <li class="colors__item" v-for="color in product.colors" :key="color.color.id">
        <label class="colors__label">
          <input class="colors__radio sr-only" type="radio"
          :value=" color.color.code" v-model="currentColor">
          <span class="colors__value" :style="{ 'background-color': color.color.code }">
          </span>
        </label>
      </li>
    </ul>
  </li>
  <button @click.prevent="getS">asdsa</button>
</template>

<script>
import axios from 'axios';
import { API_BASE_URL } from '@/config';

export default {
  data() {
    return {
      colorsData: null,

      currentColor: '',
    };
  },
  props: ['products'],
  computed: {
    
  },
  methods: {
    getS() {
      console.log(this.colors);
    },
    loadColors() {
      return axios
        .get(`${API_BASE_URL}/api/colors`)
        .then((response) => this.colorsData = response.data);
    },
  },
  created() {
    this.loadColors();
  },
};
</script>
  • Вопрос задан
  • 121 просмотр
Пригласить эксперта
Ответы на вопрос 1
Vertenz
@Vertenz
Мечтетель
Самый простой способ это добавить поле на бэке в основное тело сущности при учете определенных фильтров, например цвета. И надо декомпозировать, чтобы не тащить все вплоть до изображения. Не очень понятно что значит перво попавшуюся, вряд ли же подойдут и из других товаров и ведь нужно отталкиваться от правильно выбранного цвета?

Но если ответ всегда такой, то можно так, возьмем для пример один из присланных вами товаров и распарсим.
const jsonString = `{"items":[{"id":2,"title":"Футболка","slug":"futbolka","price":1000,"colors":[{"id":77,"color":{"id":27,"title":"white","code":"#ffffff"},"gallery":[{"file":{"url":"ссылка на изображение","name":"36efa79c31cb4400f8f9ce69a7d6f6a6.png","originalName":"Rectangle 13++.png","extension":"png","size":"63.5 Кб"}}]}],"seasons":[{"id":3,"title":"Лето","code":"summer","productsCount":10}],"materials":[{"id":1,"title":"Хлопок","code":"cotton","productsCount":6},{"id":2,"title":"Шерсть","code":"wool","productsCount":4}]}]}`;

const data = JSON.parse(jsonString);

// дальше перебором ищем где есть изображение
if (data?.items[0]?.colors.length) {
for (let i = 0; i <= data.items[0].colors.length; i++) {
   // проваливаемся в еще один цикл уже на элементы в gallery, и там ищем url, и если есть то заканчиваем цикл через r
   for (let j = 0; j <= data.items[0].colors[i].length; j++) {
     //и еще цикл на галерии
   for (let n = 0; n <= data.items[0].colors[i].gallery.length; j++) {
     if (data?.items[0]?.colors[i]?.gallery[n]?.url) {
     return data.items[0].colors[i].gallery[n].url;
   }
}

   }
}
}

// не забудьте дописать выход из всех циклов

но это выглядит очень странно, можно еще чрез forEach, но его не оставить, тч если будет миллион товаров то все упадет

items.forEach(item => {
  const keys = Object.keys(item);
  let imageKey = '';

  keys.some(key => {
    if (typeof item[key] === 'object' && item[key] !== null) {
      const subkeys = Object.keys(item[key]);
      const file = subkeys.find(subkey => {
        if (typeof item[key][subkey] === 'object' && item[key][subkey] !== null) {
          return item[key][subkey].hasOwnProperty('url');
        }
        return false;
      });
      if (file) {
        imageKey = key;
        return true;
      }
    }
    return false;
  });

  return `${item.title}"  "${imageKey}`; 
// можно дальше делать что угодно с ключем или сразу брать изображение 
});


Но честно говоря я бы еще раз подумал над ТЗ :) И точно бы вел довел бы до дочернего элемента только содержимое gallery и тогда можно обойтись без этих безумств. И проверить уже только элементы внутри gallery
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы