Ответы пользователя по тегу Vue.js
  • Как изменять значение в зависимости от нажатой кнопки с определенным шагом?

    0xD34F
    @0xD34F Куратор тега Vue.js
    data: () => ({
      value: 1,
      step: 0.25,
    }),
    methods: {
      updateValue(up) {
        const { value, step } = this;
        const rounded = Math[up ? 'floor' : 'ceil'](value / step) * step;
        this.value = rounded + step * (up ? 1 : -1);
      },
    },

    <input v-model.number="value">
    <button @click="updateValue(0)">-</button>
    <button @click="updateValue(1)">+</button>
    Ответ написан
  • Как сделать так чтобы в value компонента TreeSelect от ElementPlus попадали все выделенные элементы, а не только корневой?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Раз подобный режим работы не предусмотрен, придётся закостылить.

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

    watch(values, (newValues, oldValues) => {
      const addedValue = newValues.find(n => !oldValues.includes(n));
      if (addedValue) {
        const findAncestorValues = (arr, val, values = []) =>
          (Array.isArray(arr) ? arr : []).reduce((found, n) => {
            if (!found) {
              values.push(n.value);
              found = n.value === val ? [...values] : findAncestorValues(n.children, val, values);
              values.pop();
            }
            return found;
          }, null);
    
        values.value = [...new Set([ ...values.value, ...findAncestorValues(data, addedValue) ])];
      }
    });

    Если нежелательно реагировать на какие-либо иные изменения массива выбранных значений, кроме тех, что обусловлены действиями пользователя, то тогда вместо использования watch необходимо слушать у el-tree-select событие обновления массива выбранных значений. При этом, чтобы иметь доступ к предыдущей версии этого массива, надо будет немного поработать руками. Как-то так:

    const oldValues = ref([]);
    
    function onUpdate() {
      const addedValue = values.value.find(n => !oldValues.value.includes(n));
      if (addedValue) {
        // тут всё по-прежнему
      }
    
      oldValues.value = values.value;
    }

    <el-tree-select
      @update:modelValue="onUpdate"
      ...
    Ответ написан
  • Как скрыть все компоненты кроме одного?

    0xD34F
    @0xD34F Куратор тега Vue.js
    В корне дерева комментариев храните идентификатор того, на который в данный момент пишется ответ, отдавайте его через provide / inject во вложенные компоненты, в которых, в зависимости от равенства полученного значения конкретному id, показывайте поле для ответа.

    https://jsfiddle.net/5uw1v9o6/
    Ответ написан
    Комментировать
  • Как отобразить структуру данных в виде таблицы?

    0xD34F
    @0xD34F Куратор тега Vue.js
    А что, нормальные имена свойствам придумать было никак? Зачем все эти attr? Непонятно же ни хрена.

    Чтобы не прописывать отдельно строки с rowspan'ами и без, добавьте в v-for индексы, которые затем проверяйте в v-if ячеек с rowspan'ами - рендерить их нужно только при нулевых значениях.

    Почему выводится только objects[0]? Неужели в objects может быть только один элемент? Заверните то, что сейчас есть в ещё один template, в котором будет v-for по элементам objects.

    Можно немного сократить обращения к вложенным свойствам - для этого деструктурируйте элементы массивов, перебираемых в v-for.

    methods: {
      rowspan: attr1 => attr1.reduce((acc, n) => acc + n.attr3.length + 1, 0),
    },

    <template v-for="obj in objects">
      <template v-for="{ road, attr1, TOTAL } in obj.data">
        <template v-for="({ attr2, attr3, attr4 }, iAttr1) in attr1">
          <tr v-for="(attr3Item, iAttr3) in attr3">
            <td :rowspan="rowspan(attr1)" v-if="!iAttr1 && !iAttr3">{{ road }}</td>
            <td :rowspan="attr3.length" v-if="!iAttr3">{{ attr2 }}</td>
            <td>{{ attr3Item.road }}</td>
            <td>{{ attr3Item.cargo }}</td>
            <td>{{ attr3Item.amount }}</td>
            <td>{{ attr3Item.wo_nds }}</td>
          </tr>
          <tr>
            <td colspan="2">Итого {{ attr2 }}:</td>
            <td>{{ attr4.cargo }}</td>
            <td>{{ attr4.amount }}</td>
            <td>{{ attr4.wo_nds }}</td>
          </tr>
        </template>
        <tr>
          <td colspan="3">Итого {{ road }}:</td>
          <td>{{ TOTAL.cargo }}</td>
          <td>{{ TOTAL.amount }}</td>
          <td>{{ TOTAL.wo_nds }}</td>
        </tr>
      </template>
    </template>

    https://jsfiddle.net/ctnr98v7/4/
    Ответ написан
    3 комментария
  • После добавления v-bind с slot, он пропадает из $slots?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Конечно пропадает, он теперь в $scopedSlots.

    Ну и разберитесь с именами:

    v-bind:header="user"

    v-slot:header="{ user }"

    Привязываете параметр с именем header, а использовать пытаетесь user.
    Ответ написан
    Комментировать
  • Как остановить выполнение функции после выбора значения?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Не надо ничего "останавливать". Пусть основанием для выполнения запросов будет не любое изменение данных, а только пользовательский ввод.
    Ответ написан
    Комментировать
  • Как во Vue 3 добавить default value для input?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Редактируйте через инпут локальное значение:

    const filters = reactive({});

    <input v-model="filters.search">

    За которым установите наблюдение - там будут ваши debounce и отправка в стор:

    watch(
      () => filters.search,
      debounce(search => store.commit('setFilters', { search }), 1500)
    );

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

    watch(
      store.state.filters,
      val => Object.assign(filters, val), { immediate: true }
    );

    https://jsfiddle.net/xs40j5o1/
    Ответ написан
    Комментировать
  • Как в Vue кнопкой отключать input?

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

    const disabled = ref(false);

    <input :disabled="disabled">

    <input type="checkbox" v-model="disabled">
    Ответ написан
    Комментировать
  • Vue animation. Почему fade-enter не срабатывает?

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

    The v-enter transition class has been renamed tov-enter-from
    Ответ написан
    1 комментарий
  • Как правильно в vue3 передать событие из дочернего компонента в родительский?

    0xD34F
    @0xD34F Куратор тега Vue.js
    <RatingSelect v-model="rating" />

    const props = defineProps({
      modelValue: {
        type: Number,
        required: true,
      },
      maxRating: {
        type: Number,
        default: 10,
      },
    });
    
    const emit = defineEmits([ 'update:modelValue' ]);
    
    const selected = computed({
      get: () => props.modelValue,
      set: val => emit('update:modelValue', val),
    });

    <li v-for="n in maxRating">
      <label>
        <input type="radio" :value="n" v-model="selected">
        {{ n }}
      </label>
    </li>
    Ответ написан
  • Как сократить код во vue.js?

    0xD34F
    @0xD34F Куратор тега Vue.js
    data: () => ({
      buttons: [
        { className: 'btn1', text: 'Limit' },
        { className: 'btn2', text: 'Market' },
      ],
      active: 0,
    }),

    <button
      v-for="(n, i) in buttons"
      v-text="n.text"
      :class="[ 'btn', n.className, { 'active-btn': i === active } ]"
      @click="active = i"
    ></button>

    или даже так (это если у кнопок числа в классах btn* действительно идут по порядку)

    data: () => ({
      buttons: [ 'Limit', 'Market' ],
      active: 'Limit',
    }),

    <button
      v-for="(n, i) in buttons"
      :class="`btn btn${i + 1} ${n === active ? 'active-btn' : ''}`"
      @click="active = n"
    >{{ n }}</button>
    Ответ написан
    Комментировать
  • Как отследить клик на кластере vue-yandex-map?

    0xD34F
    @0xD34F Куратор тега Яндекс.Карты
    Вешать обработчик клика на экземпляр компонента смысла нет - он такого события не порождает. Придётся обращаться непосредственно к кластеризатору:

    <YandexClusterer
      @vue:mounted="onClustererMounted"
      ...

    methods: {
      onClustererMounted(e) {
        e.component.exposed.events.add('click', обработчикКлика);
      },
      ...

    Чтобы не ловить клики с обычных меток, надо у них запретить всплытие соответствующих событий (@click.stop).

    https://stackblitz.com/edit/vue-8tmom1?file=src%2F...
    Ответ написан
    3 комментария
  • Нужно закрыть один попап при открытии другого, как сделать?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Вместо отдельных свойств на каждый попап сделайте одно - имя открытого:

    data: () => ({
      openedPopup: null,
    }),
    methods: {
      togglePopup(popup) {
        this.openedPopup = this.openedPopup === popup ? null : popup;
      },
    },

    @click="togglePopup('search')"

    v-show="openedPopup === 'search'"
    Ответ написан
    1 комментарий
  • Как закрасить дробные части такого индикатора?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Градиентом нарисовать:

    const { filledColor: F, emptyColor: E, ...props } = defineProps({
      value: Number,
      maxValue: Number,
      segments: {
        type: Number,
        default: 5,
      },
      filledColor: {
        type: String,
        default: 'red',
      },
      emptyColor: {
        type: String,
        default: 'black',
      },
    });
    
    const background = val =>
      val >= 1 ? F :
      val <= 0 ? E :
                 `linear-gradient(to right, ${F}, ${F}, ${val * 100}%, ${E} ${val * 100}%)`;

    <div class="rating">
      <div
        v-for="i in segments"
        :style="{ background: background(value / maxValue * segments - i + 1) }"
        class="rating-segment"
      ></div>
    </div>
    Ответ написан
    Комментировать
  • Как изменить родительское значение с использованием дочернего значения VUE JS?

    0xD34F
    @0xD34F Куратор тега Vue.js
    data: () => ({
      count: 0,
    }),
    computed: {
      totalOfItem() {
        return this.count * this.price;
      },
    },
    watch: {
      totalOfItem(val, oldVal) {
        this.$emit('clicked', val - oldVal);
      },
    },

    <button @click="count--">sell</button>
    <button @click="count++">buy</button>
    Ответ написан
    Комментировать
  • Как на 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 комментария
  • Как остановить 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 комментария
  • Как получить выбранный 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(...)
    );
    Ответ написан