• Как связать два input между собой?

    0xD34F
    @0xD34F Куратор тега Vue.js
    data: () => ({
      currencies: [
        { title:  'рубль', rate:     1, val: 0 },
        { title: 'доллар', rate: 62.65, val: 0 },
        { title:   'евро', rate: 60.90, val: 0 },
      ],
    }),
    methods: {
      onInput({ rate, val }) {
        this.currencies.forEach(n => n.val = val * rate / n.rate);
      },
    },

    <div v-for="n in currencies">
      <input
        v-model.number="n.val"
        @input="onInput(n)"
        type="number"
        min="0"
      >
      {{ n.title }}
    </div>
    Ответ написан
    Комментировать
  • Как исправить ошибку "Functions are not valid as a React child"?

    0xD34F
    @0xD34F Куратор тега React
    Что я делаю не так

    В сообщении об ошибке сказано.

    как решить проблему?

    Найти у себя в коде косяк, о котором говорится в сообщении об ошибке и исправить его (как именно исправить - в сообщении об ошибке тоже кстати сказано).

    Я бы предположил, что стоит сделать следующее: { LikeIcon } ---> <LikeIcon />.
    Ответ написан
    1 комментарий
  • Как вывести значения false общим числовым количеством?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const statsMeta = [
      { title: 'отредактированные', count: item => +item.edited },
      { title: 'неотредактированные', count: item => +!item.edited },
      { title: 'всего', count: () => 1 },
    ];
    
    const statsData = allMessages.reduce((acc, n) => (
      statsMeta.forEach((m, i) => acc[i] += m.count(n)),
      acc
    ), Array(statsMeta.length).fill(0));
    
    console.log(statsMeta.map((n, i) => `${n.title}: ${statsData[i]}`).join('\n'));
    Ответ написан
    Комментировать
  • Как удалить все классы, начинающиеся с определённой подстроки?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Array.from(el.classList).forEach(n => el.classList.toggle(n, !!n.indexOf('modal--')));

    или

    el.classList.remove(...[...el.classList].filter(n => n.startsWith('modal--')));

    или

    for (let i = el.classList.length; i--; ) {
      if (el.classList[i].search('modal--') === 0) {
        el.classList.remove(el.classList[i]);
      }
    }

    или

    el.className = el.className.split(' ').filter(n => !/^modal--/.test(n)).join(' ');

    или

    el.className = el.className.replace(/(^| )modal--\S*/g, '').trim();
    Ответ написан
    9 комментариев
  • Как изменить имена ключей в массиве объектов исходя из другого массива объектов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const result = arr2.map(function(n) {
      return Object.fromEntries(this.map(m => [ m[1], n[m[0]] ]));
    }, arr1.flatMap(Object.entries));

    или

    const keys = Object.entries(Object.assign({}, ...arr1));
    const result = arr2.map(n => keys.reduce((acc, m) => (acc[m[1]] = n[m[0]], acc), {}));
    Ответ написан
    4 комментария
  • Почему при перезагрузке страницы мои данные пропадают?

    0xD34F
    @0xD34F Куратор тега React
    Встречный вопрос: почему вы решили, что должно быть иначе? Код, который бы доставал данные из localStorage и клал их в todos, где он, почему его нет? Должен быть, например:

    const [ todos, setTodos ] = useState(() => {
      let todos = null;
    
      try {
        todos = JSON.parse(localStorage.getItem('todos'));
      } catch (e) {};
    
      return Array.isArray(todos) ? todos : [];
    });

    Уверен, здесь вы ждёте, что добавляемый объект окажется в localStorage:

    setTodos([
      ...todos,
      {
        id: Date.now(),
        complete: false,
        title: value,
      },
    ]);
    localStorage.setItem("todos", JSON.stringify(todos));

    Этого происходить не будет (ну, пока ещё что-то не добавите, и это "ещё что-то" тоже попадёт в localStorage не сразу). Вы не понимаете, что происходит при вызове setTodos. Откройте документацию и разберитесь. Сохранение в localStorage следует делать через useEffect:

    useEffect(() => {
      localStorage.setItem('todos', JSON.stringify(todos));
    }, [ todos ]);
    Ответ написан
    7 комментариев
  • Как повторить каждый символ, чтобы количество повторений было равно его позиции в строке?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const xxx = str => Array
      .from(str, (n, i) => n.toUpperCase() + n.toLowerCase().repeat(i))
      .join('-');

    или

    const xxx = str => str
      .toUpperCase()
      .split('')
      .reduce((acc, n, i) => `${acc}${i ? '-' : ''}${n}${Array(-~i).join(n.toLowerCase())}`, '');

    или

    const xxx = str => str
      .toUpperCase()
      .replace(/(?<=.)./g, (m, i) => '-'.concat(m, ...Array(i).fill(m.toLowerCase())));
    Ответ написан
    Комментировать
  • Как реализовать фильтрацию на vue.js по нескольким параметрам одновременно?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Сделаем описания столбцов, по которым допускается фильтрация, и способов фильтрации - имя, русифицированный вариант имени (чтобы показывать его в select'ах), и, у столбцов, тип данных:

    data: () => ({
      filterColumns: [
        [     'name',        'имя', 'string' ],
        [    'count', 'количество', 'number' ],
        [ 'distance', 'расстояние', 'number' ],
      ],
      operations: [
        [    'equal',    'равно' ],
        [ 'contains', 'содержит' ],
        [  'greater',   'больше' ],
        [     'less',   'меньше' ],
      ],
      ...

    Тип данных указываем потому, что значения различных типов сравниваются по-разному. Кстати, определим, как именно:

    data: () => ({
      types: {
        string: {
          equal: (a, b) => a.toLowerCase() === b,
          contains: (a, b) => a.toLowerCase().includes(b),
          greater: (a, b) => a.toLowerCase() > b,
          less: (a, b) => a.toLowerCase() < b,
        },
        number: {
          equal: (a, b) => a === +b,
          contains: (a, b) => `${a}`.includes(b),
          greater: (a, b) => a > +b,
          less: (a, b) => a < +b,
        },
      },
      ...

    На основе описаний столбцов и способов фильтрации создадим select'ы:

    <select v-model="column">
      <option v-for="n in filterColumns" :value="n[0]">{{ n[1] }}</option>
    </select>
    <select v-model="operation">
      <option v-for="n in operations" :value="n[0]">{{ n[1] }}</option>
    </select>

    Наконец, собственно фильтрация - конечно же оформляем в виде вычисляемого свойства. По имени столбца, по которому надо фильтровать, извлекаем его описание и, соответственно, тип его данных. На основе типа данных и способа фильтрации извлекаем функцию фильтрации. Если таковая существует, и значение по которому надо фильтровать непустое, то выполняем фильтрацию, в противном случае возвращаем полные данные:

    computed: {
      filteredItems() {
        const { items, column } = this;
        const type = this.filterColumns.find(n => n[0] === column)?.[2];
        const filterFn = this.types[type]?.[this.operation];
        const filterVal = this.filterVal.toLowerCase();
    
        return filterFn && filterVal
          ? items.filter(n => filterFn(n[column], filterVal))
          : items;
      },
      ...

    https://jsfiddle.net/8df3z1un/1/
    Ответ написан
    Комментировать
  • Как отобразить два v-for в таблице?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Одного хватит. Который будет работать с вычисляемым свойством, представляющим объединённые данные.
    Ответ написан
    Комментировать
  • Как заменить данные карты на звездочки?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const hidden = (str, count = 4) => '*'.repeat(count) + str.slice(-4);

    или

    const hidden = (str, count = 4) => str.replace(/.*?(?=.{0,4}$)/, Array(count + 1).join('*'));
    Ответ написан
    1 комментарий
  • Какая есть библиотека для создания кастомных круглых диаграмм?

    0xD34F
    @0xD34F Куратор тега React
    Ответ написан
    Комментировать
  • Не правильно рассчитывает progress у кастомного range slider если указать min, как поправить?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Вычитайте нижнюю границу везде, где рассчитываете проценты. Например, вместо

    range.style.left = ((minPrice / rangeInput[0].max) * 100) + "%";

    должно быть

    range.style.left = ((minPrice - rangeInput[0].min) / (rangeInput[0].max - rangeInput[0].min) * 100) + '%';
    Ответ написан
    Комментировать
  • Как сложить radio и checkbox и вывести итоговой цифрой в форме на jquery?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const form = document.querySelector('form');
    
    form.addEventListener('change', () => {
      form.querySelector('.result').innerText = Array
        .from(form.querySelectorAll(':checked'))
        .reduce((acc, n) => acc + +n.getAttribute('rel'), 0);
    });
    
    form.dispatchEvent(new Event('change'));
    Ответ написан
    Комментировать
  • Как сделать чтобы при нажатии на кнопку открывался только один элемент, а не все?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Сразу определим селекторы элементов и класс, они нам дальше понадобятся:

    const blockSelector = 'селектор блоков';
    const buttonSelector = 'селектор кнопок';
    const activeClass = 'класс, показывающий блок';

    Класс надо переключать не у всех блоков сразу, а у того, который соответствует нажатой кнопке. Как определить, какой блок нужен? Можно просто по индексу. Нажали третью кнопку - нужен третий блок. Как узнать индекс кнопки? Например, он передаётся в коллбек метода forEach. Т.е., в список параметров внешнего forEach добавляем индекс, и вместо внутреннего forEach выполняем обращение к соответствующему элементу коллекции блоков:

    const blocks = document.querySelectorAll(blockSelector);
    const buttons = document.querySelectorAll(buttonSelector);
    
    buttons.forEach((n, i) => {
      n.addEventListener('click', () => {
        blocks[i].classList.toggle(activeClass);
      });
    });

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

    const onClick = function({ currentTarget: { dataset: { index } } }) {
      this[index].classList.toggle(activeClass);
    }.bind(document.querySelectorAll(blockSelector));
    
    document.querySelectorAll(buttonSelector).forEach((n, i) => {
      n.dataset.index = i;
      n.addEventListener('click', onClick);
    });

    Также есть вариант вычислять индекс при клике:

    const blocks = document.querySelectorAll(blockSelector);
    const buttons = document.querySelectorAll(buttonSelector);
    
    buttons.forEach(n => n.addEventListener('click', onClick));
    
    function onClick() {
      const index = Array.prototype.indexOf.call(buttons, this);
      blocks[index].classList.toggle(activeClass);
    }

    А вообще, если у каждой из пар кнопка-блок есть отдельный общий предок (const containerSelector = '...';), то индексы не нужны. Достаточно от нажатой кнопки подняться до общего предка кнопки и блока, и найти внутри блок (а можно даже и не искать, если добавлять класс контейнеру; конечно, стили придётся в этом случае немного изменить, вместо .блок.класс будет .контейнер.класс .блок):

    const toggleBlock = button => button
      ?.closest(containerSelector)
      ?.querySelector(blockSelector)
      .classList
      .toggle(activeClass);
    
    
    // можно добавить обработчик клика каждой кнопке индивидуально
    document.querySelectorAll(buttonSelector).forEach(function(n) {
      n.addEventListener('click', this);
    }, e => toggleBlock(e.currentTarget));
    
    // или один раз на всю страницу
    document.addEventListener('click', e => {
      toggleBlock(e.target.closest(buttonSelector));
    });
    Ответ написан
    Комментировать
  • Почему v-bind перестает работать со строками?

    0xD34F
    @0xD34F Куратор тега Vue.js
    <card-item :title="Неработающий заголовок"/>

    Понятно. Давайте посмотрим, что относительно назначения директивы v-bind говорит документация:

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

    Вопрос вам: почему вы решили, будто Неработающий заголовок - это выражение?
    Ответ написан
    3 комментария
  • Как передать в роут уникальные параметры во Vuejs?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Объекты категорий складываете в массив, массив кладёте в хранилище. Маршруту добавляете параметр - идентификатор категории. Пробрасываете его в компонент маршрута, где используете его для извлечения данных категории из хранилища. Всё.
    Ответ написан
    1 комментарий
  • Как удалить данные из массива?

    0xD34F
    @0xD34F
    $minPrices = array_values(array_reduce($arr, function($acc, $n) {
      $key = $n['code_id'];
      if (!isset($acc[$key]) || $acc[$key]['price'] > $n['price']) {
        $acc[$key] = $n;
      }
    
      return $acc;
    }, []));
    Ответ написан
    1 комментарий
  • Как используя рекурсию сконкатенировать только заглавные буквы в слово?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const getNestedItems = (data, test) =>
      data instanceof Object
        ? Object.values(data).flatMap(n => getNestedItems(n, test))
        : test(data) ? [ data ] : [];
    
    
    const result = getNestedItems(obj, x => /^[A-Z]+$/.test(x)).join('');
    Ответ написан
    4 комментария
  • Как обернуть выбранный элемент в другой, или построить вокруг него конструкцию из элементов DOM методами Vue?

    0xD34F
    @0xD34F Куратор тега Vue.js
    <div class="wrapper" v-if="надо рендерить обёртку">
      ...
      <элемент />
      ...
    </div>
    <элемент v-else />

    Или, если считаете, что "элемент" выглядит слишком сложно, чтобы его копипастить, и при этом не хотите оформлять его как отдельный компонент, пишите render-функцию.
    Ответ написан
    Комментировать