• Как на vue3 создать multy-checkbox?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Как засунуть значения выбранных чекбоксов в массив - рассказывается в документации.

    Значение для чекбокса "выбрать всё" будет вычисляемым. Ничего не выбрано - false, всё выбрано - true, для остальных вариантов undefined, при котором в true будет выставляться indeterminate. Также у него будет сеттер - если попытаться назначить true, то выбранными должны стать все чекбоксы; если false, то никто.

    Вот так:

    const items = ref([
      { id:  69, title:  'hello, world!!' },
      { id: 187, title:  'fuck the world' },
      { id: 666, title: 'fuck everything' },
    ]);
    
    const checked = ref([ 187 ]);
      
    const isAllChecked = computed({
      get: () => ({
        0: false,
        [items.value.length]: true,
      })[checked.value.length],
      set: val => checked.value = val ? items.value.map(n => n.id) : [],
    });

    <div>
      <label>
        <input
          type="checkbox"
          v-model="isAllChecked"
          :indeterminate.prop="isAllChecked === undefined"
        >
        <b>Check all</b>
      </label>
    </div>
    <div v-for="n in items" :key="n.id">
      <label>
        <input type="checkbox" v-model="checked" :value="n.id">
        {{ n.title }}
      </label>
    </div>
    Ответ написан
    3 комментария
  • Как получить подстроку между последними "/"?

    0xD34F
    @0xD34F Куратор тега JavaScript
    /[^\/]+(?=\/$)/.exec(str)[0]

    или

    str.match(/[^\/]+/g).pop()

    или

    str.split('/').at(-2)
    Ответ написан
    1 комментарий
  • Как остановить setInterval в vue 3 watch?

    0xD34F
    @0xD34F Куратор тега Vue.js
    После очистки clearInterval он чудесно продолжает работать.

    Брехня.

    Не продолжает.

    Или не он.

    О каком интервале речь?

    У вас их несколько.

    На каждое изменение длины массива - новый интервал.

    Может, не надо запускать новый, если один уже есть?
    Ответ написан
    Комментировать
  • Как остановить таймер?

    0xD34F
    @0xD34F Куратор тега Vue.js
    clearInterval из функции stopTheTimer результата необходимого не показал

    А что такое stopTheTimer? Должно быть, метод компонента. Давайте посмотрим:

    data() {
      <...>
    },
    mounted() {
      <...>
    },
    stopTheTimer() {
      clearInterval(this.interval);
    }

    Не могу назвать себя большим знатоком vue, но я точно слышал, будто методы должны находиться в methods, а не непосредственно в объекте настроек.
    Ответ написан
    3 комментария
  • Как в javascript перехватывать вызов методов объекта?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Хук get не позволяет получить параметры вызова метода

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

    function sequence(functions) {
      return new Proxy(functions, {
        get(target, key) {
          const val = target[key];
          return val instanceof Function
            ? (...args) => {
                console.log(`${key} called with arguments: `, args);
                return val.apply(target, args);
              }
            : val;
        },
      });
    }
    Ответ написан
    Комментировать
  • Как просуммировать вложенные массивы?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const uniqueWithSum = arr =>
      arr.reduce((acc, n) => {
        const keys = n.slice(0, -1);
        const item = acc.find(m => m.length === n.length && keys.every((k, i) => k === m[i]));
        (item ?? (acc[acc.length] = [ ...keys, 0 ]))[keys.length] += n[keys.length];
        return acc;
      }, []);

    или

    const uniqueWithSum = (function(arr) {
      const indexTree = new Map;
      return arr.reduce((acc, [...keys]) => {
        const val = keys.pop();
        const indexes = keys.reduce((p, c) => p.set(c, p.get(c) ?? new Map).get(c), indexTree);
        const index = indexes.set(this, indexes.get(this) ?? ~-acc.push([ ...keys, 0 ])).get(this);
        acc[index][keys.length] += val;
        return acc;
      }, []);
    }).bind(Symbol());

    или

    const uniqueWithSum = arr =>
      [...arr.reduce((acc, n) => {
        const end = n.length - 1;
        const key = n.reduce((p, c, i) => i === end ? p : p.set(c, p.get(c) ?? new Map).get(c), acc[0]);
        acc[1].set(key, acc[1].get(key) ?? n.map((m, i) => i !== end && m)).get(key)[end] += n[end];
        return acc;
      }, [ new Map, new Map ])[1].values()];
    Ответ написан
    1 комментарий
  • Как получить выбранный select в списке v-for?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Добавить select'у поддержку директивы v-model:

    props: {
      modelValue: {
        type: String,
        default: '',
      },
      ...

    <select
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)"
    >
      <option
        v-for="n in options"
        v-text="n.label"
        :value="n.value"
      ></option>
    </select>

    В родительском компоненте, сделать свойство selected вместо числа массивом и хранить там выбранные значения:

    data: () => ({
      selected: Array(10).fill(null),
      ...
    }),
    methods: {
      getValue(index) {
        this.active = index;
        console.log(this.selected[index]);
      },
    },

    <div
      v-for="(n, i) in selected"
      class="test__item"
      :class="{ test__item_active: i === active }"
    >
      <span @click="getValue(i)">элемент списка</span>
      <Select v-model="selected[i]" :options="options" />
    </div>
    Ответ написан
    Комментировать
  • Как обновить computed свойство profilesSorting с помощью watch?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Не надо никакого watch, фильтрацию выполняйте прямо в computed:

    const profilesSorting = computed(() => profiles
      .value
      .filter(n => n.id === filters.value.id)
      .sort(...)
      .slice(...)
    );
    Ответ написан
  • Как заменить ключи во вложенных объектах?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Рекурсия есть:

    const replaceKeys = (value, replacer) =>
      value instanceof Object
        ? value instanceof Array
          ? value.map(n => replaceKeys(n, replacer))
          : Object.fromEntries(Object
              .entries(value)
              .map(n => [ replacer(n[0]), replaceKeys(n[1], replacer) ])
            )
        : value;
    
    
    const newObj = replaceKeys(obj, k => `${k}_upd`);

    Рекурсии нет:

    function replaceKeys(value, replacer) {
      const stack = [];
      const clones = new Map;
      const getClone = val => val instanceof Object
        ? (clones.has(val) || stack.push([ val, clones.set(val, val.constructor()).get(val) ]),
           clones.get(val))
        : val;
    
      for (getClone(value); stack.length;) {
        const [ source, target ] = stack.pop();
        const isArray = Array.isArray(source);
        for (const k in source) if (Object.hasOwn(source, k)) {
          target[isArray ? k : replacer(k)] = getClone(source[k]);
        }
      }
    
      return getClone(value);
    }
    Ответ написан
    Комментировать
  • VueJS, при добавлении option сбивается selected по умолчанию. Как убрать этот эффект?

    0xD34F
    @0xD34F Куратор тега Vue.js
    выбранным оказывается последний добавленный элемент, несмотря на то, что всем добавленным элементам в GetSelected я вернул ' '

    Не "несмотря на то", а "потому". Пробел и пустая строка - не одно и то же.

    А вообще, ерундой какой-то занимаетесь. Может, стоит освоить использование v-model?
    Ответ написан
    5 комментариев
  • Как можно заменить цвет #a8a3a0 на пример на квадратик белого цвета?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const regex = /#[a-f\d]+;$/i;
    const replacement = '<span class="color" style="background: $&"></span>';
    
    document.querySelectorAll('селектор сами сообразите').forEach(n => {
      n.innerHTML = n.innerText.replace(regex, replacement);
    });
    Ответ написан
    2 комментария
  • Как разбить массив на подмассивы, отсортировать по рядам упорядоченных чисел?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Делаем просто, ровно то, что спрошено в вопросе:

    const result = arr.reduce((acc, n, i, a) => (
      n === a[i - 1] + 1 || acc.push([]),
      acc.at(-1).push(n),
      acc
    ), []);

    Делаем сложно, решаем задачу в более общем виде (группируем элементы не только массивов, а любых итерируемых объектов; условие создания новой группы отделяем от собственно кода группировки):

    function groupAdjacent(
      data,
      {
        key = n => n,
        newGroup = (c, p) => c !== p,
      } = {}
    ) {
      const result = [];
      const getVal = key instanceof Function ? key : n => n[key];
      let prev = null;
      let i = -1;
    
      for (const n of data) {
        const curr = getVal(n, ++i);
    
        if (!result.length || newGroup(curr, prev)) {
          result.push([]);
        }
    
        result.at(-1).push(n);
        prev = curr;
      }
    
      return result;
    }

    Как применять в вашем случае:

    const result = groupAdjacent(arr, { newGroup: (c, p) => c !== -~p });
    // или
    const result = groupAdjacent(arr, { key: (n, i) => n - i });

    Какие ещё возможны способы применения:

    groupAdjacent(arr, { key: 'name' })
    groupAdjacent(arr, { newGroup: n => n === 1 })
    groupAdjacent(arr).map(n => n.length > 1 ? n : n[0])
    groupAdjacent(arr, { newGroup: n => /^\d+\./.test(n) })
    Ответ написан
    5 комментариев
  • Почему модальное окно всплывает только при нажатие на первую кнопку?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Всплывает при нажатии на все кнопки. Но не то, которое надо, а первое. Которое видно только когда отображается первый же .product. Вместо первого .product нужен тот, внутри которого находится нажатая кнопка:

    document.querySelector('.wrapper_product') ---> event.target
    Ответ написан
    4 комментария
  • Вложенные массивы - как вытащить одно значение в цикле по самому низкому индексу?

    0xD34F
    @0xD34F
    $value = array_combine($data['value_index'], $data['value'])[min($data['value_index'])];

    или

    $value = $data['value'][array_search(min($data['value_index']), $data['value_index'])];

    или

    $index = 0;
    
    for ($i = 1; $i < count($data['value_index']); $i++) {
      if ($data['value_index'][$i] < $data['value_index'][$index]) {
        $index = $i;
      }
    }
    
    $value = $data['value'][$index];
    Ответ написан
    1 комментарий
  • Как скрывать текущий item(li) при открытие другого item(li)?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Если прямо отвечать на спрошенное, то...
    menu.addEventListener('click', ({ target: t }) => {
      if (t.tagName === 'SPAN') {
        const parent = t.parentNode;
        parent.classList.toggle('active');
        for (const n of menu.querySelectorAll('.active')) {
          if (n !== parent) {
            n.classList.toggle('active', n.contains(parent));
          }
        }
      }
    });

    Но вообще, давайте-ка напишем чуть более универсальное решение.

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

    - .menu_list_item.active .submenu {
    + .active > .submenu {

    - .submenu_list_item.active .product {
    + .active > .product {

    Ну и ещё перестанем зашивать в обработчик клика селекторы и переключаемый класс, определим их отдельно, если вдруг что изменить понадобится, то достаточно будет отредактировать код всего в одном месте:

    const container = document.querySelector('#menu');
    const itemSelector = 'li';
    const buttonSelector = `${itemSelector} span`;
    const activeClass = 'active';
    
    container.addEventListener('click', function(e) {
      const item = e.target.closest(buttonSelector)?.closest(itemSelector);
      if (item) {
        item.classList.toggle(activeClass);
        this.querySelectorAll(`.${activeClass}`).forEach(n => {
          if (n !== item) {
            n.classList.toggle(activeClass, n.contains(item));
          }
        });
      }
    });
    Ответ написан
    3 комментария
  • Как избавиться от lodash?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Открыть документацию lodash, разобраться, что делают используемые в представленном коде методы, переписать код без их использования.

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

    0xD34F
    @0xD34F Куратор тега JavaScript
    Если прямо отвечать на спрошенное, то...
    const items = document.querySelectorAll('.faq__input');
    const onChange = ({ target: t }) => t
      .closest('.faq__items')
      .querySelectorAll('.faq__question--top')
      .forEach(n => n.classList.toggle('active', n.nextElementSibling.checked));
    
    items.forEach(n => n.addEventListener('change', onChange));

    Но вообще, предлагаю назначать класс элементам, находящимся максимально высоко, самым дальним не общим предкам радиокнопок. Т.е., .faq__item вместо .faq__question--top. Если в будущем задумаете стилизовать внутри выбранного .faq__item элементы, находящиеся за пределами .faq__question--top, то не придётся переписывать js-код.

    Каким элементам надо переключать класс, что за радиокнопки находятся внутри, какой класс надо переключать, как класс переключать:

    const itemSelector = '.faq__item';
    const radioSelector = '.faq__input';
    const activeClass = 'active';
    const toggleActiveClass = radioGroupName => document
      .querySelectorAll(`${radioSelector}[name="${radioGroupName}"]`)
      .forEach(n => n.closest(itemSelector).classList.toggle(activeClass, n.checked));

    Можно назначить обработчик события каждой радиокнопке индивидуально:

    document.querySelectorAll(radioSelector).forEach(function(n) {
      n.addEventListener('change', this);
    }, e => toggleActiveClass(e.target.name));

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

    document.addEventListener('change', ({ target: t }) => {
      if (t.matches(radioSelector)) {
        toggleActiveClass(t.name);
      }
    });
    Ответ написан
    2 комментария
  • Как правильно использовать функции array_filter() и in_array()?

    0xD34F
    @0xD34F
    $values = array_column($array2, 'value');
    $result = array_filter($array1, fn($n) => in_array($n->code, $values));
    Ответ написан
    3 комментария
  • Как обратиться к ключу объекта?

    0xD34F
    @0xD34F Куратор тега Vue.js
    из дочернего мне нужно передать именно ключ b в родительский

    Не нужно.

    В родительском компоненте вы знаете, кому какой ключ соответствует. Т.е., из дочернего достаточно просто просигналить о необходимости совершения какого-то действия, а родитель дальше сам разберётся. Типа так:

    Дочерний компонент: this.$emit('do-something').
    Родительский компонент:

    <child-component :object="objects.a" @do-something="doSomething('a')" />
    <child-component :object="objects.b" @do-something="doSomething('b')" />
    <!--> или <-->
    <child-component v-for="(v, k) in objects" :object="v" @do-something="doSomething(k)" />

    methods: {
      doSomething(key) {
        // чего-то делаете
      },
      ...

    Или даже проще, без ключей. Так как, по вашим же словам, ключ вы передаёте, чтобы

    затем пройтись по objects и при совпадении ключей (a, b или c) выполнить действия над значением

    , то почему бы сразу не обращаться к нужному значению?

    <child-component :object="objects.a" @do-something="doSomething(objects.a)" />
    <child-component :object="objects.b" @do-something="doSomething(objects.b)" />
    <!--> или <-->
    <child-component v-for="n in objects" :object="n" @do-something="doSomething(n)" />

    Ещё можно отправлять из дочернего компонента наверх сам объект: this.$emit('do-something', this.object). Тогда не придётся дублировать обращение к нему в родителе:

    <child-component :object="objects.a" @do-something="doSomething" />
    <child-component :object="objects.b" @do-something="doSomething" />
    <!--> или <-->
    <child-component v-for="n in objects" :object="n" @do-something="doSomething" />
    Ответ написан
    Комментировать
  • Как сделать рекурсию для списка комментариев?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Функция, превращающая плоский массив в дерево:

    function createTree({
      arr,
      idKey = 'id',
      parentKey = 'parentId',
      childrenKey = 'children',
    }) {
      const tree = Object.fromEntries(arr.map(n => [ n[idKey], { ...n, [childrenKey]: [] } ]));
      return Object.values(tree).filter(n => !tree[n[parentKey]]?.[childrenKey].push(n));
    }

    Рекурсивный компонент v-tree, выводящий древовидные данные:

    props: {
      items: {
        type: Array,
        default: () => [],
      },
      maxdepth: {
        type: Number,
        default: Infinity,
      },
    },

    <ul v-if="Array.isArray(items) && items.length && maxdepth">
      <li v-for="n in items" :key="n.id">
        {{ n.value }}
        <v-tree :items="n.children" :maxdepth="maxdepth - 1" />
      </li>
    </ul>
    Ответ написан
    5 комментариев