Задать вопрос
Ответы пользователя по тегу Vue.js
  • Почему не получается слепить 2 полученных массива в 1?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Cannot read property 'concat' of null

    В чем ошибка и как в итоге их объединить?

    Так написано же в чём - не удаётся прочитать свойство concat у null. Вы пытаетесь выполнять какие-то действия над свойствами компонента, как над массивами, но данные ещё не загрузились, и они (свойства) всё ещё null.

    Выполняйте объединение массивов только в том случае, если оба свойства не null:

    computed: {
      fullArray() {
        const { users, info } = this;
    
        return user && info
          ? здесь объединяете данные массивов
          : [];
      },
    },

    Или установите в качестве начальных значений пустые массивы вместо null.
    Ответ написан
    Комментировать
  • Как вывести линию canvas vue.js?

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

    0xD34F
    @0xD34F Куратор тега Vue.js
    Фильтр или метод сделайте:

    data: () => ({
      date: new Date(),
    }),
    filters: {
      formatDate1: d => d.toLocaleString('ru-RU').replace(',', '').slice(0, -3),
    },
    methods: {
      formatDate2: d => [ 'Date', 'Month', 'FullYear', 'Hours', 'Minutes' ]
        .map((n, i) => '.. :'.charAt(~-i) + `${d[`get${n}`]() + !~-i}`.padStart(2, 0))
        .join(''),
    },

    <div class="fullDate">{{ date | formatDate1 }}</div>
    <div class="fullDate">{{ formatDate2(date) }}</div>
    Ответ написан
    1 комментарий
  • Почему Vue рендерит компонент вне родительского блока?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Особенности парсинга DOM-шаблона:

    Некоторые HTML-элементы, такие как <ul>, <ol>, <table> и <select> имеют ограничения на то, какие элементы могут отображаться внутри них, или например элементы, такие как <li>, <tr> и <option> могут появляться только внутри других определённых элементов.

    Это приведёт к проблемам при использовании компонентов с элементами, которые имеют такие ограничения. Например:

    <table>
      <blog-post-row></blog-post-row>
    </table>

    Пользовательский компонент <blog-post-row> будет поднят выше, так как считается недопустимым содержимым, вызывая ошибки в итоговой отрисовке. К счастью, специальный атрибут is предоставляет решение этой проблемы:

    <table>
      <tr is="blog-post-row"></tr>
    </table>
    Ответ написан
    Комментировать
  • Как вызвать событие родительского объекта из шаблона(template) vue js?

    0xD34F
    @0xD34F Куратор тега Vue.js
    В дочернем компоненте:

    <input @input="$emit('fucking-event')">

    В родителе:

    <fucking-component @fucking-event="onFuckingEvent">

    methods: {
      onFuckingEvent() {
        console.log('fuck off');
      },
    Ответ написан
    Комментировать
  • Как написать имя события в подписчике v-on: в виде выражения?

    0xD34F
    @0xD34F Куратор тега Vue.js
    v-on="{ [здесь собираете имя события]: handler }"
    Ответ написан
    Комментировать
  • Как в Vue подставить значение в input?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Ну, НАВЕРНОЕ, надо присвоить значение свойству, которое вы указали в v-model.
    Ответ написан
    3 комментария
  • Как динамически изменять размеры блока?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Нет такого атрибута - transform. Это должно быть свойство style:

    :style="{ transform: computedMatrix }"
    Ответ написан
    Комментировать
  • Как данный код переписать на vue?

    0xD34F
    @0xD34F Куратор тега Vue.js
    В первую очередь следует избавиться от копипасты в разметке. Видим группу схожих элементов - оставляем один, вешаем на него v-for, которому будет передаваться массив с данными. Блок базовой комплектации примет примерно такой вид:

    baseOptions: [
      { name: 'Свойство 1', price: 10000 },
      { name: 'Свойство 2', price: 11000 },
      ...

    <ul>
      <li v-for="n in baseOptions">{{ n.name }} ({{ n.price }})</li>
    </ul>

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

    extraOptions: [
      { name: 'Пакет 1', price: 16000, checked: false },
      { name: 'Пакет 2', price: 17000, checked: false },
      ...

    <div v-for="n in extraOptions">
      <input type="checkbox" v-model="n.checked">
      <label>{{ n.name }}</label>
    </div>


    Сделаем метод расчёта суммы. Он будет принимать массив опций и складывать их стоимость:

    methods: {
      sum: arr => arr.reduce((acc, n) => acc + n.price, 0),

    С помощью этого метода посчитаем суммы базовой комплектации и дополнительных опций. Оформим их как вычисляемые свойства:

    computed: {
      baseSum() {
        return this.sum(this.baseOptions);
      },
      extraSum() {
        return this.sum(this.extraOptions.filter(n => n.checked));
      },


    Наконец, разберёмся с выводом сумм. Сделаем фильтр, где число будет приводиться к человекопонятному формату и добавляться наименование валюты:

    filters: {
      price: val => `${val.toLocaleString()} р.`,

    <span>{{ baseSum | price }}</span>

    <span>{{ extraSum | price }}</span>

    https://jsfiddle.net/3gmrs0o1/1/
    Ответ написан
  • Почему infoWindow в vue-google-map не открывается?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Не открывается, потому что вместо изменения значения свойства компонента, отвечающего за состояние infoWindow, у вас в методе onMarkerClick какая-то ересь.

    Делайте так:

    <gmap-info-window 
      :options="infoOptions" 
      :position="infoWindowPos" 
      :opened="infoWinOpen"
      @closeclick="infoWinOpen = false"
    >
      {{ infoContent }}
    </gmap-info-window>

    onMarkerClick(e) {
      this.infoWindowPos = e.latLng; // задаём положение окна, над кликнутым маркером
      this.infoContent = JSON.stringify(e.latLng); // задаём контент окна, передаётся в слот
      this.infoWinOpen = true; // открываем окно
    },
    Ответ написан
    1 комментарий
  • Клик вне области компонента vue?

    0xD34F
    @0xD34F Куратор тега Vue.js
    https://www.npmjs.com/package/v-click-outside

    Ну или самостоятельно можете это дело реализовать:

    created() {
      const onClickOutside = e => this.opened = this.$el.contains(e.target) && this.opened;
      document.addEventListener('click', onClickOutside);
      this.$on('hook:beforeDestroy', () => document.removeEventListener('click', onClickOutside));
    },
    Ответ написан
    2 комментария
  • Как сгруппировать массив объектов по месяцам?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Функция получения имени месяца по его индексу, она нам ниже понадобится:

    const getMonthName = iMonth =>
      new Date(0, iMonth).toLocaleString('ru-RU', { month: 'long' });

    Сгруппированные данные оформляем в виде вычисляемого свойства.

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

    computed: {
      groupedByMonth() {
        return this.items.reduce((acc, n) => {
          const key = getMonthName(n.date.split('-')[1] - 1);
          (acc[key] = acc[key] || []).push(n);
          return acc;
        }, {});
      },
    },

    <ul>
      <li v-for="(items, month) in groupedByMonth" :key="month">
        <h3>{{ month }}</h3>
        <ul>
          <li v-for="n in items" :key="n.id">{{ n }}</li>
        </ul>
      </li>
    </ul>

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

    groupedByMonth() {
      return this.items.reduce((acc, n) => {
        acc[n.date.split('-')[1] - 1].items.push(n);
        return acc;
      }, Array.from({ length: 12 }, (_, i) => ({
        month: getMonthName(i),
        items: [],
      })));
    },

    <ul>
      <li v-for="{ items, month } in groupedByMonth" :key="month">
        <h3>{{ month }}</h3>
        <ul v-if="items.length">
          <li v-for="n in items" :key="n.id">{{ n }}</li>
        </ul>
        <div v-else>данных нет</div>
      </li>
    </ul>
    Ответ написан
    6 комментариев
  • Как динамически менять url адрес api?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Импортируете роутер в своём store.js:

    import router from './router.js';

    При создании url смотрите текущий маршрут:

    const url = `/api/photos?${в зависимости от router.currentRoute}=true&page=${list}&limit=15`;
    Ответ написан
  • Как создать маркер по клику GoogleMap в Vue?

    0xD34F
    @0xD34F Куратор тега Vue.js
    <gmap-map
      ref="map"
      v-bind="options"
      @click="onMapClick"
    >
      <gmap-marker
        v-for="m in markers"
        :key="m.id"
        :position="m.position"
        :clickable="true"
        :draggable="true"
        @click="onMarkerClick"
      />
    </gmap-map>

    data: () => ({
      options: {
        center: { lat: 45.101637, lng: 38.986345 },
        zoom: 15,
      },
      markers: [],
    }),
    methods: {
      onMapClick(e) {
        this.markers.push({
          id: 1 + Math.max(0, ...this.markers.map(n => n.id)),
          position: e.latLng,
        });
      },
      onMarkerClick(e) {
        this.$refs.map.panTo(e.latLng);
        // или
        // this.options.center = e.latLng;
      },
    },

    https://codesandbox.io/s/blissful-fast-pjc7o
    Ответ написан
    1 комментарий
  • Как лучше всего передавать данные из компонентов с множеством инпутов родителю?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Отправляйте наверх не только значение, но и имя параметра. Сами параметры соберите в один объект - так проще и ещё можно будет использовать v-model.
    Ответ написан
    1 комментарий
  • Как во Vue сделать один из чекбоксов по умолчанию checked и disabled?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Вырезаете selectedProducts, вместо него используете в v-model свойства checked элементов products. Отключить чекбокс - свойства disabled у вас уже есть, просто привяжите их значения к чекбоксам.

    <input
      type="checkbox"
      v-model="product.checked"
      :disabled="product.disabled"
    >

    Ну и в отсутствие selectedProducts следует переписать вычисляемое свойство totalPrice:

    computed: {
      totalPrice() {
        return this.products.reduce((acc, n) => acc + n.checked * n.price, 0);
      },
    },
    Ответ написан
    Комментировать
  • Как динамически создавать элементы во Vue.js?

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

    <component
      v-for="n in flyingButtons"
      :is="n.tag"
      :class="n.class"
    >
      {{ n.title }}
    </component>
    Ответ написан
    Комментировать
  • Как сделать пагинацию через Vuex?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Хранилище

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

    state: {
      page: 0,
      perPage: 5,
      total: 0,
      posts: [],
      loading: false,
    },

    Понадобится геттер, вычисляющий количество страниц:

    getters: {
      numPages: state => Math.ceil(state.total / state.perPage),
    },

    Что и как будет обновляться в стейте:

    mutations: {
      updateLoading: (state, loading) => state.loading = loading,
      updatePosts: (state, { posts, total, page }) => Object.assign(state, { posts, total, page }),
    },

    Загрузка данных - до запроса устанавливаете loading в true, после переключаете обратно в false; запрос, сохранение результатов - тут, думаю, всё понятно:

    actions: {
      async fetchPosts({ state, commit }, page) {
        commit('updateLoading', true);
    
        const start = (page - 1) * state.perPage;
        const end = page * state.perPage;
        const url = `https://jsonplaceholder.typicode.com/posts?_start=${start}&_end=${end}`;
    
        try {
          const response = await fetch(url);
          const posts = await response.json();
          const total = response.headers.get('x-total-count');
          commit('updatePosts', { posts, total, page });
        } catch (e) {
          console.error(e);
        }
    
        commit('updateLoading', false);
      },
    },

    Компонент пагинации

    Получает два параметра - номер текущей страницы и количество страниц:

    props: [ 'page', 'numPages' ],

    Имеет методы для перехода к конкретной странице (отправляет номер новой страницы в родительский компонент - тому виднее, что с ним делать) и к странице относительно текущей:

    methods: {
      goTo(page) {
        this.$emit('paginate', page);
      },
      next(step) {
        this.goTo(this.page + step);
      },
    },

    Чтобы не давать пользователю переходить к несуществующим страницам, сделаем вычисляемые свойства, указывающие, является ли текущая страница первой/последней,...

    computed: {
      isFirst() {
        return this.page <= 1;
      },
      isLast() {
        return this.page >= this.numPages;
      },
    },

    ...и используем эти свойства для блокировки кнопок перехода на предыдущую/следующую/первую/последнюю страницы:

    <button @click="goTo(1)" :disabled="isFirst">&lt;&lt;</button>
    <button @click="next(-1)" :disabled="isFirst">&lt;</button>
    {{ page }} / {{ numPages }}
    <button @click="next(+1)" :disabled="isLast">&gt;</button>
    <button @click="goTo(numPages)" :disabled="isLast">&gt;&gt;</button>

    Ну и добавим поддержку директивы v-model:

    model: {
      prop: 'page',
      event: 'paginate',
    },


    Как всем этим пользоваться

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

    computed: {
      page: {
        get() {
          return this.$store.state.page;
        },
        set(page) {
          this.$store.dispatch('fetchPosts', page);
        },
      },

    Привязываем через v-model это свойство к экземпляру компонента пагинации (ну и количество страниц не забываем ему передать):

    <компонент-пагинации
      v-model="page"
      :num-pages="$store.getters.numPages"
    />

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

    created() {
      this.page = 1;
    },

    Отображение данных:

    <div v-if="$store.state.loading">данные загружаются, ждите</div>
    <компонент-для-отображения-данных v-else :данные="$store.state.posts" />


    https://jsfiddle.net/08hno57q/
    Ответ написан
    Комментировать
  • Vue.js Калькулятор цен. Как справиться с radio кнопками?

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