Ответы пользователя по тегу Vue.js
  • Как переписать Boolean значение в props?

    0xD34F
    @0xD34F Куратор тега Vue.js
    В родительском компоненте выполняйте привязку параметра isOpen с помощью директивы v-model, замените :is-open="item.isOpen" на v-model:is-open="item.isOpen".

    Соответственно, в дочернем компоненте вместо прямого изменения значения параметра отправляйте новое значение родителю, замените isOpen = !isOpen на $emit('update:isOpen', !isOpen).
    Ответ написан
    Комментировать
  • Как отфильтровать массив объектов по ключам?

    0xD34F
    @0xD34F Куратор тега Vue.js
    computed: {
      grouped() {
        return this.arr.reduce((acc, n) => ((acc[n.group] ??= []).push(n), acc), {});
      },
    },

    <v-expansion-panels>
      <v-expansion-panel v-for="(items, groupName) in grouped">
        <v-expansion-panel-header>{{ groupName }}</v-expansion-panel-header>
        <v-expansion-panel-content>
          <div v-for="n in items">{{ n.name }}</div>
        </v-expansion-panel-content>
      </v-expansion-panel>
    </v-expansion-panels>
    Ответ написан
    7 комментариев
  • Как сделать фильтрацию древовидных массивов?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Функция фильтрации получает массив и строку поиска, рекурсивно вызывается для дочерних элементов каждого из элементов переданного массива. Возвращает элементы, чей name содержит строку поиска или у кого после рекурсивного вызова остались дочерние элементы.

    const filter = (arr, str) => (arr || [])
      .map(n => ({ ...n, children: filter(n.children, str) }))
      .filter(n => n.name.includes(str) || n.children.length);

    Применительно к vue - оформляете отфильтрованный массив как вычисляемое свойство:

    computed: {
      filteredItems() {
        return filter(this.items, this.search);
      },
    },

    https://jsfiddle.net/pL2fh506/
    Ответ написан
    Комментировать
  • Как сделать чтобы при переключении checkbox, он прибавлял какое-то значение к переменной?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Сделайте total вычисляемым свойством:

    computed: {
      total() {
        return this.value1 * this.value2 + this.checkbox * 1000;
      },
    },
    Ответ написан
    3 комментария
  • Vue 3 как обратиться к компоненту через this.$refs?

    0xD34F
    @0xD34F Куратор тега Vue.js
    const tree = ref(null)

    Начало уже есть, хорошо. Теперь надо этот tree вернуть из setup'а, чтобы им можно было пользоваться в шаблоне:

    return {
      tree,
      ...

    <el-tree
      ref="tree"
      ...

    Ну а когда захотите получить выбранные значения - tree.value.getCheckedKeys().
    Ответ написан
    1 комментарий
  • Какой есть плагин валидации телефона на vue?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Это называется не валидация, а маска. Погуглил за вас:

    https://www.npmjs.com/package/v-mask
    https://primevue.org/inputmask/
    https://niksmr.github.io/vue-masked-input/
    Ответ написан
    Комментировать
  • Как в дочернем компоненте узнать, что закончилась асинхронная загрузка данных?

    0xD34F
    @0xD34F Куратор тега Vue.js
    В App.vue я запрашиваю все продукты из БД

    Если продукты должны быть доступны везде, может, тогда стоит сначала их получить, а потом уже инициализировать приложение:

    store.dispatch('GET_PRODUCTS').then(() => {
      new Vue({ ... });
    });

    ??

    Если же отвечать непосредственно на спрошенное - установите наблюдение за значением в хранилище:

    watch: {
      '$store.state.products': {
        immediate: true,
        handler(products) {
          if (products) {
            // можно что-то сделать
          }
        },
      },
    },
    Ответ написан
    1 комментарий
  • Как передать модель в кастомный switch Vue js?

    0xD34F
    @0xD34F Куратор тега Vue.js
    1. Убрать:

      data() {
        return {
          isChecked: false,
        }
      },
      methods: {
        toggleSwitch() {
          this.isChecked = !this.isChecked
          this.$emit('input', this.isChecked)
        },
      },


    2. Добавить:

      props: {
        value: Boolean,
      },


    3. Заменить:

      :class="{ 'ivu-switch-checked': isChecked }" на :class="{ 'ivu-switch-checked': value }"

      @click.prevent="toggleSwitch" на @click.prevent="$emit('input', !value)"

    Ответ написан
    Комментировать
  • Почему при удалении элемента класс check не удаляется?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Потому что кто-то невнимательно читал документацию.

    UPD. Как-то сразу не догадался глянуть js-код. А там, а там... ЭТО ЧТО ЕЩЁ ЗА НА ХРЕН:

    elem.classList.add('check');
    elem.querySelector('input[type="checkbox"]').remove();

    ??

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

    0xD34F
    @0xD34F Куратор тега Vue.js
    Не могу закрыть поп-ап

    dialog: [],

    dialog[index]= false

    Кто-то невнимательно читал документацию.

    При нажатии на кнопку, при выборе 3 изображения на превью открывается всегда первое.

    Не всегда, а при первом открытии каждого диалога.

    Кстати, непонятно - а зачем вам три диалоговых окна? Сделайте одно:

    data: () => ({
      slide: null,
      dialog: false,
      ...
    }),
    methods: {
      openDialog(slideIndex) {
        this.slide = slideIndex;
        this.dialog = true;
      },
      ...
    },

    v-row
      v-col(v-for="(n, i) in pictures")
        v-item
          v-img(height="100", width="100", :src="n", @click="openDialog(i)")
      v-col(hidden="")
        v-dialog(v-model="dialog")
          v-carousel(v-model="slide")
            v-carousel-item(v-for="n in pictures")
              v-img(:src="n")
          v-btn(@click="dialog = false") close dialog
    Ответ написан
    1 комментарий
  • Как передать props в компонент во Vue 3 через роутинг?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Сначала пара слов о том, как правильно:

    Роутинг для передачи данных в этой ситуации использовать не надо. Подключаете vuex, кладёте массив в хранилище, компонент home никаких параметров не принимает, просто достаёт из хранилища нужные данные и передаёт их в компонент posts:

    <posts :posts="$store.state.posts" />

    Как видите, всё элементарно.

    Передача же "через роутинг" обернётся говнокодерством:

    • Можно сделать маршрут именованным и передавать через params:

      <router-link :to="{ name: 'home', params: { posts } }">home</router-link>

      this.$router.push({
        name: 'home',
        params: {
          posts: this.posts,
        },
      });

      Сразу же очевиден первый недостаток такого способа: в скольких местах осуществляется переход на роут home, столько раз придётся и передавать туда явным образом массив posts, указывая его в params каждого router-link или вызова $router.push. При этом, если переход осуществляется не из корневого компонента, то, чтобы дотянуться до posts, придётся ещё и $root использовать. В случае же, когда пользователь решит осуществить переход посредством адресной строки браузера, то ничего передать не получится.

      Ещё один недостаток станет очевидным после перехода - оказывается, элементы posts превратились в строки. Роутер приводит к строкам значения свойств params (или, если свойство является массивом, к строкам будут приведены его элементы). Так что для корректной передачи ещё придётся и в json данные конвертировать, а в компоненте home этот json парсить - говнокод, как и обещал.

    • Можно прицепить данные к router-view, тогда они будут переданы как параметры в компонент текущего маршрута: <router-view :posts="post" />.

      Тут тоже не всё пройдёт гладко - так передача параметров будет осуществляться в компоненты всех маршрутов, и там, где переданные параметры не описаны, они будут сочтены атрибутами и прицеплены к корневому элементу (если он один; когда несколько - надо явным образом указывать, куда добавлять $attrs). Чтобы передавать что надо только куда надо, можно оформить параметры, передаваемые в router-view, как вычисляемое свойство, значение которого будет зависеть от текущего маршрута:

      computed: {
        routeParams() {
          return что-то, в зависимости от this.$route;
        },
      },

      <router-view v-bind="routeParams" />

    Ответ написан
  • Как сделать добавление tr в таблицу по условию?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Такие есть варианты:

    1. Сделать отдельный tbody для каждой пары строк. Переносите tbody из компонента таблицы в компонент строки - будет там корневым элементом:

      <tbody>
        <tr>... </tr>
        <tr v-if="...">...</tr>
      </tbody>

      Пример.

    2. Сделать компонент строки функциональным - там может быть несколько корневых узлов. Пример.
    3. Отказаться от использования компонента строки, рендерить tr сразу в компоненте таблицы:

      <tbody>
        <template v-for="...">
          <tr>... </tr>
          <tr v-if="...">...</tr>
        </template>
      </body>

      Пример.

    4. Переехать на Vue 3 - там можно сделать компонент с несколькими корневыми узлами. Пример.
    5. Попробовать использовать костыль. Здесь примера не будет - к этой штуке не прикасался и не собираюсь.
    Ответ написан
    Комментировать
  • Как создать динамический шаблон при компонентном подходе без создания и дальнейшего перебора массива в массиве в еще одном массиве?

    0xD34F
    @0xD34F Куратор тега Vue.js
    А откуда там взяться массиву в массиве и т.д.? Нужен массив объектов, которые описывают поля формы, а также объект с данными формы (это то, что сейчас у вас представлено отдельными свойствами). Всё:

    data: () => ({
      formData: {},
      formMeta: [
        { name: 'phone', title: 'Телефон', type: 'text', placeholder: 'Телефон' },
        { name: 'email', title: 'E-mail', type: 'text', placeholder: 'E-mail' },
        { name: 'address', title: 'Адрес', type: 'text', placeholder: 'Адрес' },
        ...
      ],
    }),

    this.$emit('save-contact', this.formData);

    <div v-for="n in formMeta">
      <span class="form__other-title">{{ n.title }}</span>
      <div class="form__other-data">
        <input
          v-model="formData[n.name]"
          :type="n.type"
          :placeholder="n.placeholder"
        >
      </div>
    </div>
    Ответ написан
  • Как создать табы на vue?

    0xD34F
    @0xD34F Куратор тега Vue.js
    работать с dom напрямую плохая практика

    Не всегда. Зависит от задачи.

    Пусть компонент табов принимает в качестве параметров массив значений, описывающих табы и значение, обозначающее активный таб:

    props: [ 'items', 'value' ],

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

    data: () => ({
      sliderStyles: null,
    }),

    Создадим сами табы и элемент подчёркивания:

    <ul class="tabs">
      <li
        v-for="n in items"
        :key="n.value"
        @click="$emit('input', n.value)"
        class="tabs-item"
      >{{ n.text }}</li>
    </ul>
    <div
      class="tabs-slider"
      :style="sliderStyles"
    ></div>

    Элементу подчёркивания (.tabs-slider) зададим абсолютное позиционирование и transition.

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

    mounted() {
      this.$watch(
        'value',
        value => {
          const index = this.items.findIndex(n => n.value === value);
          const el = this.$el.querySelectorAll('.tabs-item')[index];
          this.sliderStyles = el
            ? {
                left: `${el.offsetLeft}px`,
                width: `${el.offsetWidth}px`,
              }
            : null;
        },
        {
          immediate: true,
        }
      );
    },

    https://jsfiddle.net/vsr5tLue/
    Ответ написан
    Комментировать
  • Как во vue js сделать аналог foreach?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Никакой "аналог foreach" тут не нужен.

    Сделайте компонент: получает через слот контент, который надо показывать/скрывать; содержит свойство, в зависимости от значения которого переданный контент отображается или нет; переключает значение свойства, управляющего видимостью контента, по клику. Как-то так:

    data: () => ({
      opened: false,
    }),

    <div class="dropdown">
      <div class="dropdown-header" @click="opened = !opened">
        <slot name="header" :opened="opened">
          {{ opened ? 'CLOSE' : 'OPEN' }}
        </slot>
      </div>
      <div v-if="opened" class="dropdown-content">
        <slot name="content"></slot>
      </div>
    </div>

    Ну а дальше можете создавать экземпляры этого компонента - сколько вам надо и где надо. Всё.
    Ответ написан
    Комментировать
  • Как правильно передавать и принимать пользовательские события во вложенных компонентах?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Обработчик события прицеплен к какому-то непонятному div'у вместо экземпляра компонента. Неудивительно, что он не вызывается.
    Ответ написан
    1 комментарий
  • Как объединить два чарта в Chart.js?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Надо определить две оси Y:

    options: {
      scales: {
        yAxes: [
          { id: 'y1', position: 'left' },
          { id: 'y2', position: 'right' },
        ],
      },
    },

    приложение делается на vue

    Есть vue-chartjs, использование которого для вашего случая может выглядеть так, например.
    Ответ написан
  • Как центрировать карту на маркере по клику на item списка маркеров?

    0xD34F
    @0xD34F Куратор тега Яндекс.Карты
    Свойство coords - переместить из компонента с картой в vuex. Тогда в компоненте элемента списка можно будет делать что-то вроде @click="$store.commit('setCoords', marker.coords)".
    Ответ написан
  • Как сделать сортировку в таблице по отдельной кнопке?

    0xD34F
    @0xD34F Куратор тега Vue.js
    data: () => ({
      sortBy: '',
      ...
    }),

    <v-btn @click="sortBy = 'имя_свойства_по_которому_надо_выполнить_сортировку'">click me</v-btn>
    
    <v-data-table :sort-by.sync="sortBy">...</v-data-table>

    Если при повторном клике надо переключать порядок сортировки, тогда так:

    data: () => ({
      sortBy: [],
      sortDesc: [],
      ...
    }),
    methods: {
      sort(colName) {
        const sameCol = this.sortBy[0] === colName;
        const sortDesc = this.sortDesc[0];
    
        this.sortBy = sameCol && sortDesc ? [] : [ colName ];
        this.sortDesc = sameCol && sortDesc ? [] : [ sameCol ];
      },
      ...
    },

    <v-btn @click="sort('имя_свойства_по_которому_надо_выполнить_сортировку')">click me</v-btn>
    
    <v-data-table :sort-by.sync="sortBy" :sort-desc.sync="sortDesc">...</v-data-table>

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

    sort(colName) {
      const index = this.sortBy.indexOf(colName);
      if (index === -1) {
        this.sortBy.push(colName);
        this.sortDesc.push(false);
      } else if (this.sortDesc[index]) {
        this.sortBy.splice(index, 1);
        this.sortDesc.splice(index, 1);
      } else {
        this.$set(this.sortDesc, index, true);
      }
    },

    А вообще, можно сделать совсем просто. Имитируется клик по заголовку указанного столбца, дальше компонент таблицы сам всё исполнит в лучшем виде:

    <v-btn @click="sort('имя_свойства_по_которому_надо_выполнить_сортировку')">click me</v-btn>
    
    <v-data-table ref="table">...</v-data-table>

    methods: {
      sort(colName) {
        const index = this.свойство_описывающее_столбцы_таблицы.findIndex(n => n.value === colName);
        this.$refs.table.$el.querySelector('thead tr').cells[index].click();
      },
      ...
    },
    Ответ написан
    Комментировать
  • Как во vuex передать инстанс яндекс.карты, чтобы в методах компонента Vue можно было по клику добавлять маркеры?

    0xD34F
    @0xD34F Куратор тега Яндекс.Карты
    Не нужно дёргать карту напрямую. Сделайте массив с данными маркеров, заполняйте его по клику, и на его основе создавайте экземпляры компонента маркера:

    import { yandexMap, ymapMarker, loadYmap } from 'vue-yandex-maps';

    components: {
      yandexMap,
      ymapMarker,
    },
    data: () => ({
      coords: null,
      markers: [],
      settings: { /* ... */ },
    }),
    methods: {
      onClick(e) {
        this.markers.push({
          id: 1 + Math.max(0, ...this.markers.map(n => n.id)),
          coords: e.get('coords'),
        });
      },
    },
    async mounted() {
      await loadYmap({ ...this.settings, debug: true });
    
      ymaps.geolocation.get().then(res => {
        this.coords = res.geoObjects.position;
      });
    },

    <yandex-map
      v-if="coords"
      :coords="coords"
      @click="onClick"
    >
      <ymap-marker
        v-for="n in markers"
        :key="n.id"
        :marker-id="n.id"
        :coords="n.coords"
      ></ymap-marker>
    </yandex-map>

    https://jsfiddle.net/f65hraxk/
    Ответ написан
    3 комментария