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

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

    Встречный вопрос - почему вы решили, что внутреннее состояние компонента обновляться должно? Покажите, где вы обновляете targetDate при изменении props.date. Нет, можете себя не утруждать - ничего такого у вас нет.

    Не надо никакого targetDate, рассчитываем дни-часы-минуты-секунды сразу на основе props.date - так при изменении props.date не придётся предпринимать никаких дополнительных телодвижений, всё посчитается как надо при следующем вызове updateCountdown. Вот как-то так.
    Ответ написан
    Комментировать
  • Как убрать scroll у body при открытии модальных окон через vue watch?

    0xD34F
    @0xD34F Куратор тега Vue.js
    .no-overflow {
      overflow: hidden;
    }

    mounted() {
      this.$watch(
        () => this.isModalAddVisible || this.activeId,
        val => document.body.classList.toggle('no-overflow', val),
        { immediate: true }
      );
    },

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

    0xD34F
    @0xD34F Куратор тега Vue.js
    Данные слайдов сложить в массив. Сделать вычисляемое свойство - данные всех слайдов кроме верхнего. Создать верхний слайд, создать остальные слайды на основе вычисляемого свойства. Если нужна анимация перемещения, завернуть слайды в transition-group. Вот так всё просто.
    Ответ написан
    1 комментарий
  • Как динамически добавлять текстовые поля в форму редактирования строки таблицы с использованием Quasar?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Сложить имена основных полей в массив.

    Сделать вычисляемое свойство, представляющее имена дополнительных полей - надо получить список ключей editedItem и выкинуть из него имена основных полей.

    В диалоговом окне сделать v-for по массиву дополнительных полей.

    Всё.
    Ответ написан
    Комментировать
  • Как получить свойство из props drilling по открытию модального окна?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Не надо никаких v-model, кого показывать в модальном окне - отправляйте наверх эту информацию вместе с событием. Свойство, управляющее видимостью модального окна - пусть оно вместо логического значения хранит id или объект или что там у вас должно показываться в окне, если не null, значит открываем окно. Вот так всё просто.

    UPD. Вот вариант с v-model, конечно только на уровне таблицы, в строках всё по-прежнему.

    UPD. А вообще, можно полностью отказаться от прокидывания событий наверх. Таблицу исполняем в более общем виде - пусть принимает массив с описанием столбцов, а содержимое ячеек задаётся через слоты, имена которых определяются на основе имён столбцов (по умолчанию выводят данных как есть). В этом случае кнопку можно определить там же, где создаётся экземпляр таблицы. Как это может выглядеть - ваш вариант с отдельным компонентом строки (и, соответственно, прокидыванием в него слотов из компонента таблицы), или, без ненужного усложнения.
    Ответ написан
    1 комментарий
  • Добавил элементы в массив в local storage, при обновлении страницы компоненты пропадают с экрана, но остаются в local storage, как это исправить?

    0xD34F
    @0xD34F Куратор тега Vue.js
    const addCard = (cardName) => {
      cards.value.push({
        id: Date.now(),
        component: cardName,
        order: cards.value.length + 1,
        isRequired: false
      })
    }

    Что такое cardName? Не то, что вам, судя по имени параметра, кажется. Это не имя, а сам объект компонента. И вы его потом в localStorage засовываете. Так что методы setup и render теряются.

    Сложите компоненты в объект:

    const components = {
      ShortTextCard,
      LongTextCard,
      SingleQuestionCard,
      MultiQuestionCard,
    };

    Передавайте в addCard их имена:

    - @click="addCard(ShortTextCard)"
    + @click="addCard('ShortTextCard')"

    Когда надо экземпляр компонента создать, доставайте объект компонента по его имени из упомянутого выше объекта:

    - :is="card.component"
    + :is="components[card.component]"
    Ответ написан
    1 комментарий
  • Как совместить переход по вкладкам из element plus и vue-router?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Лучше никак.

    Так что сперва о том, как надо. Вместо вкладок используем меню - оно умеет работать с роутером:

    <el-menu mode="horizontal" router :default-active="$route.name">
      <el-menu-item
        v-for="n in $router.getRoutes()"
        v-text="n.name"
        :index="n.name"
        :route="n"
      />
    </el-menu>
    <router-view />

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

    computed: {
      activeRouteName: {
        get() {
          return this.$route.name;
        },
        set(name) {
          this.$router.push({ name });
        },
      },
    },

    Используем это свойство для управления вкладками. Контент у всех вкладок одинаковый - <router-view>, но рендерить будем его только в активной вкладке. Вот такой получается говнокод:

    <el-tabs v-model="activeRouteName">
      <el-tab-pane v-for="{ name: n } in $router.getRoutes()" :label="n" :name="n">
        <router-view v-if="activeRouteName === n" />
      </el-tab-pane>
    </el-tabs>
    Ответ написан
    Комментировать
  • Как в el-table вставить нужный мне id, для перехода на страницы?

    0xD34F
    @0xD34F Куратор тега Vue.js
    handleEdit(row){

    @click="handleEdit(scope.$index, scope.row)"

    Сколько параметров получает метод handleEdit? А сколько передаёте ему в обработчике клика? Устраните это печальное несоответствие.

    path:'/protocol_information/:id/edit/',
    params:{
        id:row.id
    }

    Вместо path должен быть name. Конечно, если таковой указан при определении маршрута.

    Или, вырезаете params и вставляете нужный id сразу в строку:

    path: `/protocol_information/${row.id}/edit/`,
    Ответ написан
  • Как из родителя сбросить :checked в дочерних компонентах?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Снизу вверх изменения отслеживаются, а сверху вниз - нет, состояние дочерних компонентов никак не зависит от родителя.

    Вырезать обработчики события change и, соответственно, методы filtered; свойства currentPriority и currentStopsId сделать вычисляемыми - геттеры возвращают значение параметра, сеттеры отправляют новое значение родителю:

    currentPriority: {
      get() {
        return this.filterPriorities;
      },
      set(val) {
        this.$emit('update:filterPriorities', val);
      },
    },

    currentStopsId: {
      get() {
        return this.filterStops;
      },
      set(val) {
        this.$emit('update:filterStops', val);
      },
    },
    Ответ написан
    1 комментарий
  • Не могу удалить элемент input из списка - всегда удаляется последний?

    0xD34F
    @0xD34F Куратор тега Vue.js
    v-for="(address, i) in form.addresses_to" :key="i"

    всегда удаляется последний

    Конечно последний. Индекс в качестве ключа - хуже, чем ничего. Индекс ключом быть не может, думаете, что ключи есть, а на самом деле их нет. А если ключей нет, то

    Vue использует алгоритм, который минимизирует перемещение элементов

    Т.е., было в массиве N значений, на основе которых создано N элементов. Одно значение из массива выкидывается, значит, и элементов должно остаться N - 1. Какой удалить проще всего? - последний, не надо будет перемещать элементы, расположенные за ним.

    Сделайте нормальные ключи - добавьте объектам address свойства id, имеющие уникальные значения, и используйте их в качестве ключей.
    Ответ написан
    Комментировать
  • Как организовать динамическую форму?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Метаданные формы - массив объектов, содержащих имя поля, имя компонента, с которым будет взаимодействовать пользователь, объект параметров для компонента:

    const formMeta = ref([
      {
        name: '...',
        component: '...',
        props: { ... },
      },
      ...
    ]);

    Данные формы - объект, в качестве ключей будут выступать значения свойств name элементов formMeta. Изначально можно сделать пустым: const formData = ref({});. Можно явно задать начальные значения:

    const formData = ref({
      имяПоля1: значение1,
      имяПоля2: значение2,
      ...
    });

    Или есть вариант доставать ключи и дефолтные значения из метаданных:

    const formData = ref(Object.fromEntries(formMeta.value.map(n => [
      n.name,
      n.defaultValue ?? null,
    ])));

    На основе formMeta создаётся форма, через v-model свойства formData связываются с экземплярами компонентов:

    <form>
      <div v-for="n in formMeta">
        <component
          :is="components[n.component]"
          v-model="formData[n.name]"
          v-bind="n.props"
        />
      </div>
    </form>

    Вот как-то так.
    Ответ написан
    1 комментарий
  • Как вывести сложную таблицу во Vue.js?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Если количество уровней вложенности невелико и известно заранее, то... Для обработки каждого уровня вложенности используется отдельный <template v-for, ячейки с rowspan'ами создаются в зависимости от равенства индексов элементов вложенных массивов нулю, значения rowspan'ов - длины (суммы длин) вложенных массивов. Например:

    const rowspan = item => item.backlinks.reduce((acc, n) => acc + n.recipients.length, 0);

    <tbody>
      <template v-for="item in data">
        <template v-for="(backlink, iBacklink) in item.backlinks">
          <template v-for="(recipient, iRecipient) in backlink.recipients">
            <tr>
              <template v-if="!iBacklink && !iRecipient">
                <td :rowspan="rowspan(item)">{{ item.name }}</td>
                <td :rowspan="rowspan(item)">{{ item['domain score'] }}</td>
              </template>
              <template v-if="!iRecipient">
                <td :rowspan="backlink.recipients.length">{{ backlink.donor }}</td>
                <td :rowspan="backlink.recipients.length">{{ backlink['page score'] }}</td>
              </template>
              <td>{{ recipient.url }}</td>
              <td>{{ recipient.image }}</td>
            </tr>
          </template>
        </template>
      </template>
    </tbody>

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

    const keys = ref([
      'name', 'domain score', 'backlinks',
      'donor', 'page score', 'recipients', 
      'url', 'image',
    ]);

    function createTableData(arr, keys, iKey = 0) {
      return arr.flatMap(n => {
        const row = [];
        const innerRows = [];
    
        for (let i = iKey; i < keys.length; i++) {
          const val = n[keys[i]];
          if (Array.isArray(val)) {
            innerRows.push(...createTableData(val, keys, i + 1));
            row.forEach(cell => cell.rowspan = innerRows.length);
            row.push(...innerRows.shift());
            break;
          } else {
            row.push({ text: val });
          }
        }
    
        return [ row, ...innerRows ];
      });
    }

    Преобразование вложенных данных в плоские можно оформить в виде вычисляемого свойства:

    const tableData = computed(() => {
      return createTableData(props.data, props.keys);
    });

    Ну а построить таблицу на основе плоского массива - дело тривиальное:

    <tbody>
      <tr v-for="row in tableData">
        <td
          v-for="cell in row"
          v-text="cell.text"
          :rowspan="cell.rowspan"
        ></td>
      </tr>
    </tbody>
    Ответ написан
    Комментировать
  • Как вывести списком данные?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Рекурсивный компонент. Проверяем, является ли переданное значение объектом. Если нет - отображаем его. Если да - бежим по его свойствам, под каждое создаём по экземпляру компонента. Например, так.
    Ответ написан
    Комментировать
  • Как переписать код перемещения блока мышью с чистого js на vue?

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

    const coords = ref([ 50, 50 ]);
    
    const circleStyles = computed(() => ({
      left: `${coords.value[0]}px`,
      top: `${coords.value[1]}px`,
    }));
    
    const updateCoords = e => coords.value = [ e.pageX, e.pageY ];
    const updateCoordsOn = () => document.addEventListener('mousemove', updateCoords);
    const updateCoordsOff = () => document.removeEventListener('mousemove', updateCoords);

    <div
      class="circle"
      :style="circleStyles"
      @mousedown="updateCoordsOn"
      @mouseup="updateCoordsOff"
    ></div>
    Ответ написан
    Комментировать
  • Почему не работает двусторонняя привязка с родительским компонентом во vue2?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Потому что возможность указывать аргумент для v-model появилась в третьей версии vue.

    Вместо v-model используйте sync.
    Ответ написан
    3 комментария
  • Как подключить и использовать внешний JS скрипт Choices.js в Vue.js компонент?

    0xD34F
    @0xD34F Куратор тега Vue.js
    <div v-once>
      <select ref="select" v-model="region"></select>
    </div>

    mounted() {
      const choices = new Choices(this.$refs.select);
      this.$watch(
        'regions',
        val => choices.setChoices(val, 'value', 'name', true),
        { immediate: true }
      );
      this.$on('hook:beforeDestroy', () => choices.destroy());
    },
    Ответ написан
  • Как проверить загружается ли изображение?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Назначайте класс в зависимости от значения свойства, которое будет устанавливаться в обработчиках событий load и error. Например.
    Ответ написан
    1 комментарий
  • Миграция на VUE 3, не работает роутер?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Для vue 3 предназначена четвёртая версия роутера, а не третья:

    - <script src="https://unpkg.com/vue-router@3/dist/vue-router.js"></script>
    + <script src="https://unpkg.com/vue-router@4/dist/vue-router.global.js"></script>

    А вот это надо вырезать:

    import VueRouter from 'vue-router'
    Ответ написан