• Поиск на сайте. Как сделать "ничего не найдено"?

    0xD34F
    @0xD34F Куратор тега Vue.js
    const filteredProducts = computed(() => {
      const search = searchQuery.value.toLowerCase();
      return search.length > 2
        ? data.value.filter(n => n.title.toLowerCase().includes(search))
        : null;
    });

    <ul v-if="filteredProducts" class="search-result">
      <li v-if="!filteredProducts.length">
        <h3>ничего не найдено</h3>
      </li>
      <li v-for="n in filteredProducts">
        <div>
          <Image :src="n.image" :alt="n.title" width="40" height="40" />
        </div>
        <h3>{{ n.title }}</h3>
        <div>
          <p>{{ n.price }}</p>
        </div>
      </li>
    </ul>
    Ответ написан
    1 комментарий
  • Vue3 как показать скрыть элемент внутри v-for?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Показываем не более одного - добавляем в компонент свойство, которое будет содержать id, индекс или ещё что-то уникальное для каждого из элементов данных (да хоть бы и ссылку на сам элемент). Показываем скрытое в зависимости от равенства значения этого свойства тому, что доступно в текущей итерации v-for.

    Или, показываем несколько сразу - создаём отдельный компонент, в экземпляры которого будут передаваться элементы данных. Внутри компонента свойство, в зависимости от значения которого (истина-ложь) показываем или скрываем чего там надо.
    Ответ написан
    Комментировать
  • Как найти центральную ячейку таблицы?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Как получить строки таблицы:

    // если гарантируется отсутствие thead и tfoot, или их содержимое также должно учитываться
    const { rows } = table;
    
    // если tbody один
    const [ { rows } ] = table.tBodies;
    
    // если tbody несколько
    const rows = Array.prototype.flatMap.call(table.tBodies, n => [...n.rows]);
    // или
    const rows = [].concat(...Array.from(table.tBodies, n => [...n.children]));
    // или
    const rows = table.querySelectorAll('tbody tr');

    Как получить средний элемент:

    const middle = arr => arr[arr.length >> 1];
    // или
    const middle = arr => arr[Math.floor(arr.length / 2)];
    // или
    const middle = arr => arr[Math.round(~-arr.length / 2)];
    // или
    const middle = arr => arr[(arr.length - arr.length % 2) / 2];

    Всё, можно доставать ячейку:

    const cell = middle(middle(rows).cells);
    // или
    const cell = middle([].reduce.call(rows, (acc, n) => (acc.push(...n.children), acc), []));
    // или, без получения строк
    const cell = middle(table.querySelectorAll('tbody td'));
    Ответ написан
    Комментировать
  • Как зациклить эффект печати?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Строки сложить в массив. Завести два индекса - текущей строки и её последнего отображаемого символа. Если при увеличении количества отображаемых символов выходим за границу строки - переключаемся на следующую строку и сбрасываем счётчик символов.

    Куда, что и с какой задержкой будем печатать:

    const el = document.querySelector('p');
    const strings = [ 'hello, world!!', 'fuck the world', 'fuck everything' ];
    const delay = 100;

    Печатаем:

    function Typewriter(el, strings, delay) {
      let i = 0;
      let length = 0;
    
      return setInterval(() => {
        if (++length > strings[i].length) {
          i = -~i % strings.length;
          length = 0;
        }
    
        el.textContent = strings[i].slice(0, length);
      }, delay);
    }
    
    
    const intervalId = Typewriter(el, strings, delay);
    // хотим остановить, делаем так: clearInterval(intervalId);

    или

    function Typewriter(el, strings, delay) {
      let timeoutId = null;
    
      (function step(i, length) {
        length = -~length % -~strings[i].length;
        i = (i + !length) % strings.length;
        el.innerText = strings[i].substring(0, length);
        timeoutId = setTimeout(step, delay, i, length);
      })(0, 0);
    
      return () => clearTimeout(timeoutId);
    }
    
    
    const stop = Typewriter(el, strings, delay);
    // хотим остановить, делаем так: stop();
    Ответ написан
    1 комментарий
  • Как при нажатии на кнопку из ul удалить li с максимальным значением?

    0xD34F
    @0xD34F Куратор тега JavaScript
    При повторном нажатии на кнопку максимальным числом выскакивает число удалённого li.

    Как и должно быть. Кто вам обещал, что вызов метода remove приведёт к исчезновению элемента из lis? Искать максимальный элемент следует среди тех, что всё ещё присутствуют на странице, а не тех, что были изначально - то есть, или надо получать элементы непосредственно перед поиском, или коллекция элементов должна быть динамической.

    Сам поиск максимального элемента - это какая-то шизофрения, какого чёрта вы удаляете элементы до того, как будут проверены значения всех (т.е., до того, как убедитесь, что тот, который собираетесь удалить, действительно максимальный - попробуйте сделать максимальный не первым, и посмотрите, что получится)? При переборе коллекции надо запоминать максимальный элемент, сравнивать с текущим, если текущий больше, обновлять максимум.

    function max(data, key = n => n) {
      const getVal = key instanceof Function ? key : n => n[key];
      let result = null;
    
      for (const n of data) {
        const val = getVal(n);
        if (!result || result[1] < val) {
          result = [ n, val ];
        }
      }
    
      return result?.[0];
    }
    
    
    document.querySelector('button').addEventListener('click', function(e) {
      max(this, n => +n.textContent)?.remove();
      e.target.disabled = !this.length;
    }.bind(document.querySelector('ul').children));
    Ответ написан
  • Как удалить поведение в третьей версии яндекс карт?

    0xD34F
    @0xD34F Куратор тега Яндекс.Карты
    Получить массив текущих поведений, выкинуть лишнее, установить:

    map.setBehaviors(map.behaviors.filter(n => n !== 'drag'));
    Ответ написан
    1 комментарий
  • Как сделать увеличение карты по клику на стороннюю кнопку?

    0xD34F
    @0xD34F Куратор тега Яндекс.Карты
    - if (map && typeof map.setCenter === 'function' && typeof map.setZoom === 'function') {
    -     map.setCenter(15)(marker.geometry.getCoordinates(), 15);
    -     map.setZoom(15);
    - }
    + map.setLocation({
    +   center: markersCollection.find(n => n.id === officeId).coordinate,
    +   zoom: 15,
    + });
    Ответ написан
    1 комментарий
  • Как сделать динамический текст над слайдером?

    0xD34F
    @0xD34F Куратор тега React
    использую библиотеку rc-slider, к сожалению она не предлагает возможности...

    А если изучить документацию повнимательнее? Есть пример с выводом значения в тултипе при перетаскивании, сделать тултип постоянно видимым не проблема.
    Ответ написан
    1 комментарий
  • Как сложить два 16-ных числа?

    0xD34F
    @0xD34F
    1 переносим на следующий разряд влево, а 5 пишем в текущий

    Единица - окей, а пятёрка откуда взялась? F + B будет 1A. Ну, то есть 15 + 11 = 26. Т.е., 16 (единица, переносящаяся влево) и 10 (A).
    Ответ написан
  • Как вывести общую сумму в корзине?

    0xD34F
    @0xD34F Куратор тега Vue.js
    item.isAdded = true

    Это как, одни и те же объекты используются и в списке товаров, и в корзине? Можете начинать думать, как будете переделывать, чтобы такой ерунды не было. Ну а пока попробуем заставить ваш говнокод вести себя должным образом:

    const quantity = ref(1)

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

    const quantity = defineModel('quantity');

    В родителе ловим это новое количество и дёргаем метод обновления:

    <CartItem
      v-for="n in cart"
      v-bind="n"
      :key="n.id"
      @update:quantity="updateQuantity(n, $event)"
      @remove="removeFromCart(n)"
    />

    Откуда этот метод возьмётся? Оттуда же, откуда и методы добавления (этот вам придётся поправить самостоятельно, при добавлении в корзину надо задавать количество по умолчанию, типа единица) и удаления, делаем по аналогии:

    const updateQuantity = (item, quantity) => item.quantity = quantity;

    Ну и наконец, исправленный расчёт суммы, цену надо умножать на количество:

    const totalPrice = computed(() => cart.value.reduce((acc, n) => acc + n.price * n.quantity, 0));
    Ответ написан
    Комментировать
  • Как сгруппировать массив в одномерный с соединением ключей и значений?

    0xD34F
    @0xD34F
    function getCombinations($arr, $keys = [], $vals = []) {
      return ($n = $arr[count($keys)] ?? null)
        ? array_merge(...array_map(
            fn($k) => getCombinations(
              $arr,
              [ ...$keys, $k ],
              [ ...$vals, ...$n[$k] ]
            ),
            array_keys($n)
          ))
        : [ implode('_', $keys) => $vals ];
    }
    Ответ написан
    1 комментарий
  • Как сделать прогресс бар для опросника с разными ответами в зависимости от вопроса и его ответа на alpine js?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Добавить счётчик полученных ответов, делать ему +/- 1 при переходах к следующему/предыдущему вопросу.

    При расчёте прогресса проверять, что текущий вопрос последний (т.е., список возможных следующих вопросов пуст), в зависимости от результата проверки возвращать чего там вам надо - сразу 100 или умножаете счётчик ответов на 10 (кстати, уверены, что больше девяти вопросов задано не будет?).

    UPD. Как избавиться от этой ерунды с умножением на 10 - давайте будем вычислять максимальное количество вопросов, которые ещё могут быть заданы. Тогда текущий прогресс будет выражаться отношением количества отвеченных вопросов к сумме отвеченных и оставшихся.

    А ещё давайте избавимся от поиска вопросов по их id - для этого следующие/предыдущие вопросы надо хранить в виде индексов вместо id, или вместо массива вопросов сделать объект, где ключами будут id.

    Как это может выглядеть:

    Рекурсия есть:

    getMaxQuestionsLeft(question, depth) {
      return Math.max(
        depth,
        ...question.nextQuestions.map(n => {
          return this.getMaxQuestionsLeft(this.questions[n], depth + 1);
        })
      );
    },
    get progress() {
      const ac = this.answersCount;
      return Math.floor(ac / (ac + this.getMaxQuestionsLeft(this.question, 0)) * 100);
    },

    Или, рекурсии нет:

    get maxQuestionsLeft() {
      let result = 0;
    
      for (const stack = [ [ this.question, 0 ] ]; stack.length;) {
        const [ q, d ] = stack.pop();
        result = Math.max(result, d);
        stack.push(...q.nextQuestions.map(n => [ this.questions[n], -~d ]));
      }
    
      return result;
    },
    get progress() {
      const { answersCount: ac } = this;
      return ac / (ac + this.maxQuestionsLeft) * 100 | 0;
    },
    Ответ написан
    2 комментария
  • Vue-router не изменяет url страницы при нажатии RouterLink. Содержимое меняется. В чем может быть проблема?

    0xD34F
    @0xD34F Куратор тега Vue.js
    history: createMemoryHistory(),

    Vue-router не изменяет url страницы

    А должен? Лично я слышал, что нет:

    The memory history mode doesn't assume a browser environment and therefore doesn't interact with the URL
    Ответ написан
    Комментировать
  • Как создать фильтрацию через select на vue 3 с composition api?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Вот так:

    Компонент FilterBlock
    const status = defineModel('status');
    const statuses = [ 'all', 'alive', 'dead', 'unknown' ];

    <select v-model="status">
      <option v-for="n in statuses">{{ n }}</option>
    </select>

    Компонент СardList
    defineProps({
      data: {
        type: Array,
        default: () => [],
      },
    });

    <div v-for="n in data">
      <span :class="[ 'status', n.status ]"></span>
      ...
    </div>

    .status {
      border-radius: 50%;
      width: 30px;
      height: 30px;
    
      &.alive { background: green; }
      &.dead { background: red; }
      &.unknown { background: orange; }
    }

    Компонент App
    const data = ref([ ... ]);
    const status = ref('all');
    const filteredData = computed(() => status.value === 'all'
      ? data.value
      : data.value.filter(n => n.status === status.value)
    );

    <FilterBlock v-model:status="status" />
    <CardList :data="filteredData" />
    Ответ написан
    Комментировать
  • Почему не получается отфильтровать массив строк?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Предлагаю сделать следующее:

    • Прочитать сообщение об ошибке и попытаться осознать, что в нём сказано
    • Посмотреть, какие значения принимает key при переборе p
    • Открыть документацию и разобраться, что делает цикл for ... in
    • Погуглить, какие ещё бывают способы перебора
    Ответ написан
    Комментировать
  • Как посчитать сумму одинаковых объектов в списке словарей?

    0xD34F
    @0xD34F
    def unique_with_sum(arr, id_key, sum_key):
      unique = {}
    
      for n in arr:
        unique.setdefault(n[id_key], { **n, sum_key: 0 })[sum_key] += n[sum_key]
    
      return [*unique.values()]
    
    
    result = unique_with_sum(arr, 'uid_1c', 'amount')
    Ответ написан
    1 комментарий
  • Почему не работает v-model для компонента?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Версия vue какая?

    Если 3, то всё ясно:

    BREAKING: When used on custom components, v-model prop and event default names are changed:
    • prop: value -> modelValue;
    • event: input -> update:modelValue;

    Если 2 - явных косяков не видно, воспроизводите проблему в песочнице, ссылку сюда.
    Ответ написан
    1 комментарий
  • Как преобразовать JSON файл в другой JSON файл?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Прочитать содержимое файла, распарсить, выполнить необходимые преобразования, превратить данные обратно в строку, записать в файл.

    Как видите, всё довольно просто.
    Ответ написан
    Комментировать
  • Как получить массив дней месяца, с пн по вс?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const getWeekdaysOfMonth = (year, month) =>
      Array.from(
        { length: new Date(year, month, 0).getDate() },
        function() {
          this[0].setDate(-~this[0].getDate());
          this[1] += this[0].getDay() === 1 || !this[1];
          return `неделя ${this[1]}, ` + this[0].toLocaleString('ru-RU', {
            day: 'numeric',
            weekday: 'short',
          });
        },
        [ new Date(year, ~-month, 0), 0 ]
      );
    
    
    const may2024 = getWeekdaysOfMonth(2024, 5);
    const sep2023 = getWeekdaysOfMonth(2024, -3);
    const jun2021 = getWeekdaysOfMonth(2020, 18);
    Ответ написан
    Комментировать
  • Как переместить колонку с checkbox вправо, в компоненте QTable, Quasar?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Что-то не видать в документации соответствующей настройки (UPD. Да её и нет, глянул в исходниках, как происходит добавление колонки с чекбоксами - unshift, и ничего больше). Давайте закостылим - добавляем колонку, которая будет дублировать данный функционал, а оригинальную спрячем:

    const columns = [
      ...
      { name: 'selection' },
    ];

    .xxx tr > :first-child {
      display: none;
    }

    <q-table
      ...
      table-class="xxx"
    >
      <template #header-cell-selection="props">
        <q-th :props="props">
          <q-checkbox v-model="props.selected" />
        </q-th>
      </template>
    
      <template #body-cell-selection="props">
        <q-td :props="props">
          <q-checkbox v-model="props.selected" />
        </q-td>
      </template>
    </q-table>
    Ответ написан
    1 комментарий