Ответы пользователя по тегу Vue.js
  • Почему при удалении элемента класс check не удаляется?

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

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

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

    ??

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

    UPD. Как может выглядеть исправленный (и дополненный - редактирование теперь тоже есть) вариант вашего кода:

    data: () => ({
      tasks: [],
      taskId: 0,
      newTaskText: '',
    }),
    methods: {
      addTask() {
        this.tasks.push({
          id: ++this.taskId,
          text: this.newTaskText,
          done: false,
          edit: false,
        });
    
        this.newTaskText = '';
      },
    },

    <ul>
      <li v-for="(n, i) in tasks" :key="n.id" :class="{ check: n.done }" @dblclick="n.edit = !n.done">
        <input type="checkbox" v-if="!n.done" v-model="n.done">
        <input type="text" v-if="!n.done && n.edit" v-model="n.text" @keyup.enter="n.edit = false">
        <template v-else>{{ n.text }}</template>
        <input type="button" class="del" value="+" @click="tasks.splice(i, 1)">
      </li>
    </ul>
    <input type="text" v-model="newTaskText" @keyup.enter="addTask">
    Ответ написан
  • Как правильно настроить слайдер и поп-ап 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 комментария
  • Как правильно добавить фотографии в слайдер Vue Carousel 3D?

    0xD34F
    @0xD34F Куратор тега Vue.js
    data: () => ({
      slides: [
        'путь к первой картинке',
        'путь ко второй картинке',
        ...
      ],
    }),

    <slide v-for="(n, i) in slides" :index="i">
      <figure>
        <img :src="n">
      </figure>
    </slide>
    Ответ написан
    Комментировать
  • Как показывать только один компонент, который используется несколько раз?

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

    data: () => ({
      items: [ ... ],
      active: null,
    }),

    <v-xxx
      v-for="(n, i) in items"
      :показыватьДополнительныйКонтент="active === i"
      @переключитьОтображениеДополнительногоКонтента="active = active === i ? null : i"
      ...

    https://jsfiddle.net/kdg4qevp/

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

    const xxx = Vue.observable({ active: null });

    В свой компонент добавьте вычисляемое свойство, значение которого а) определяет необходимость демонстрации дополнительных элементов; б) зависит от свойства наблюдаемого объекта:

    computed: {
      показыватьДополнительныйКонтент() {
        return xxx.active === this.какоеТоСвойствоСУникальнымЗначением;
      },
    },

    <div v-if="показыватьДополнительныйКонтент">
      ...
    </div>

    Также добавьте в свой компонент метод для переключения отображения дополнительного содержимого:

    methods: {
      переключитьОтображениеДополнительногоКонтента() {
        xxx.active = this.показыватьДополнительныйКонтент
          ? null
          : this.какоеТоСвойствоСУникальнымЗначением;
      },
    },

    <button @click="переключитьОтображениеДополнительногоКонтента">
      {{ показыватьДополнительныйКонтент ? 'hide' : 'show' }}
    </button>

    https://jsfiddle.net/kdg4qevp/1/
    Ответ написан
    1 комментарий
  • Что означают три точки впереди функции во vue шаблоне?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Это означает, что вы не знаете js.

    Да и в целом - плохо понимаете смысл используемых вами слов. Какой шаблон, вы чего? Шаблон vue - это html, который компилируется в render-функцию. А вы что показали?
    Ответ написан
    4 комментария
  • Vuetify - как обратиться к чекбоксам?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Добавляете в компонент свойство, которое будет указывать, случалось ли ваше событие. Если его значение false, тогда ничего не делаете, по умолчанию будет отрендерен чекбокс, если true - передаёте в соответствующий слот что там вам надо:

    data: () => ({
      событиеНаступило: false,
      ...
    }),

    @событие="событиеНаступило = true"

    <v-data-table>
      <template v-if="событиеНаступило" #item.data-table-select>
        здесь размещаете контент, который должен отображаться вместо чекбоксов
      </template>
      ...

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

    <v-data-table>
      <template #item.data-table-select="{ item, isSelected, select }">
        <div v-if="item.событиеНаступило">hello, world!!</div>
        <v-simple-checkbox
          v-else
          :value="isSelected"
          @input="select($event)"
        ></v-simple-checkbox>
      </template>
      ...
    Ответ написан
  • Почему в диалоговых окнах данные повторяются?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Не повторяются.

    Поскольку управление отображением всех диалогов завязано на одно и то же свойство (v-model="dialog_destroy"), то при открытии любого из них открываются все и вы всегда видите только последний - он скрывает собой остальные.

    Пусть видимостью диалогов управляют разные свойства:

    data: () => ({
      showDialog: {},
      ...
    }),

    <v-dialog v-model="showDialog[item.свойствоИмеющееУникальныеЗначенияДляКаждогоItem]">

    <v-btn @click="showDialog[item.свойствоИмеющееУникальныеЗначенияДляКаждогоItem] = false">Отменить

    Можно и без дополнительного объекта обойтись, хранить информацию о состоянии диалога среди прочих данных:

    <v-dialog v-model="item.showDialog">

    <v-btn @click="item.showDialog = false">Отменить

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

    data: () => ({
      dialogData: null,
      ...
    }),

    <v-data-table>
      <template #item.action="{ item }">
        <v-btn @click="dialogData = item">Удалить контакт</v-btn>
      </template>
    </v-data-table>
    
    <v-dialog :value="!!dialogData" @input="dialogData = $event">
      <span>Удалить контакт: @{{ dialogData?.contact_name }}</span>
      <v-btn :href="dialogData?.destroy_link">Подтвердить</v-btn>
      <v-btn @click="dialogData = null">Отменить</v-btn>
    </v-dialog>
    Ответ написан
  • Передача css классов в компонент - вывести эти классы не на root элементе, на дочернем?

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

    functional: true,
    render(h, ctx) {
      return h('div', {
        class: 'col-12',
      }, [
        h('input', {
          class: [ ctx.data.class, ctx.data.staticClass ],
        }),
      ]);
    },
    Ответ написан
    Комментировать
  • Каким способом можно сделать автосоздание переменных для v-model?

    0xD34F
    @0xD34F Куратор тега Vue.js
    mounted() {
      this.$el.querySelectorAll('input').forEach(n => n.dispatchEvent(new Event('input')));
    },

    jsfiddle.net/whrgboxs
    Ответ написан
    Комментировать
  • Как сделать попеременное нажатие чекбоксов?

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

    inheritAttrs: false,
    model: {
      prop: 'checked',
    },
    props: [ 'value', 'checked' ],
    computed: {
      innerChecked: {
        get() {
          return this.checked;
        },
        set(val) {
          this.$emit('input', val);
        },
      },
    },

    <label>
      <input
        type="checkbox"
        v-model="innerChecked"
        v-bind="$attrs"
        :value="value"
      >
      <slot></slot>
    </label>

    Снаружи:

    data: () => ({
      items: [
        { label:  'hello, world!!', value:  69 },
        { label:  'fuck the world', value: 187 },
        { label: 'fuck everything', value: 666 },
      ],
      checked: [ 187 ],
    }),
    watch: {
      checked(val) {
        if (val.length > 1) {
          this.checked = val.slice(-1);
        }
      },
    },

    <v-checkbox
      v-for="n in items"
      v-model="checked"
      :value="n.value"
      name="items[]"
    >
      {{ n.label }}
    </v-checkbox>

    https://jsfiddle.net/769wdoq5/

    Один вопрос - на хрена? Можно заменить чекбоксы радиокнопками, и добавить ещё одну, которая будет обозначать отсутствие выбора.
    Ответ написан
    Комментировать