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

    @karambafe
    Стандартная проблема с кастомными инпутами и валидацией в форме.

    Одно из решений - в форме создать один массив ошибок с поинтерами, указывающими на определенный input. Особенно это хорошо, когда бекенд на этапе проверки формы возвращает ошибки тоже с конкретными поинтерами.

    Пример кастомного инпута c выводом текста ошибки под ним. Если есть хоть одна ошибка, то навешивается "ошибочный" класс со своими стилями.
    <template>
      <div class="input">
        <input
          type="text"
          class="input__field"
          :class="{ 'input__field_error': errors.length > 0 }"
          :placeholder="placeholder"
          :value="value"
          :disabled="disabled"
          @input="handleInput"
        >
    
        <div class="input__errors">
          <p
            v-for="(error, index) in errors"
            :key="index"
            class="input__error"
          >
            {{ error }}
          </p>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      name: 'custom-input',
      props: {
        disabled: {
          type: Boolean,
          default: false,
        },
        errors: {
          type: Array,
          default: () => ([]),
        },
        value: {
          type: String,
          default: '',
        },
        placeholder: {
          type: String,
          default: '',
        },
      },
      methods: {
        handleInput({ target: { value } }) {
          this.$emit('onInput', value);
        },
      },
    };
    </script>


    А в форме уже мы распределяем все ошибки по нужным инпутам:
    <template>
    <form @onSubmit.prevent="handleSubmit">
      <custom-input
         :value="phone"
         :disabled="loading"
         :errors="phoneErrors" // передаем только относящиеся к конкретному инпуту ошибки
         @onInput="handleInput"
      />
    
      <button type="submit">Отправить</button>
    </form>
    </template>
    
    <script>
    export default {
      data: () => ({
         phone: '',
         errors: [],
         loading: false,
      }),
      computed: {
         phoneErrors() {
           // этот код лучше вынести в хелпер, чтобы потом переиспользовать с другими поинтерами
            if (this.errors.length === 0) return [];
            
            return this.errors
             .filter(error => error.pointer === 'phone') // проверка на нужный поинтер
             .map(item => item.detail); // делаем массив только из текста ошибок
         },
      },
      methods: {
        handleInput(newPhone) {
         // при вводе нового значения обнуляем все ошибки. 
         // Можно сделать обнуление ошибок по конкретному поинтеру
          this.errors = []; 
          this.phone = newPhone;
        },
        handleSubmit() {
            this.errors = [];
    
           // Если инпут пустой, то сами генерируем ошибку
           // и не делаем отправку данных на сервер
           if (this.phone.length === 0) {
            this.errors = [...this.errors, {
              detail: 'Телефон не может быть пустым',
              pointer: 'phone',
            }];
            return;
          }
    
          // Если же все ок, то отправляем данные на сервер
          this.loading = true;
          this.sendPhone();
        },
      },
    };
    </script>


    Возможно это и многословный код, но он хорошо читается, понятен и крайне легко покрывается unit тестами
    Ответ написан
    2 комментария
  • Как исправить ошибки vue-masonry?

    @karambafe
    Такая ошибка обычно возникает, когда в шаблоне используется переменная, которая не объявлена в компоненте в data/props/computed.

    В вакууме тяжело сказать ошибка это плагина или кода внутри компоненты.
    Сделал маленький пример, где просто подключил библиотеку и заюзал директиву v-mansory - никаких ошибок в консоли нет (codesanbox), так что скорее всего надо искать косяки в шаблоне компоненты
    Ответ написан
  • Как в компоненте получить данные из хранилища?

    @karambafe
    Если вы хотите подписаться на данные из store, то используйте computed properties:

    export default {
      computed: {
        productName() {
           return this.$store.state.products.map(product => product.name);
        },
      },
    };


    В store же вообще не надо заводить отдельный стейт productName, потому что это вычисляемое значение относительно стейта products - можно объявить как отдельный getter productName в Vuex, а можно получить через computed в компоненте как описано выше

    Геттеры Vuex
    Ответ написан
  • Nuxt.js Как разделить приложение?

    @karambafe
    Явным образом нельзя, по крайней мере в документации об этом ни слова, да и при создании нового приложения через стартер есть выбор universal (SSR+CSR) или client-side only mode.

    Стандартным решением данного вопроса является разделение на два разных приложения:
    1. Морда (особенного если это простенький лендос) делается на любой технологии (тот же nuxtjs в режиме генерации статики).
    2. Админка делается на любом client-side фреймворке (Vue, React, etc), потому что ей обычно не нужно SEO и нет повышенных требований к скорости загрузки, время tti и тд. "Поселить" ее можно на поддомене.

    Таким образом получится 2 статических приложения, а значит не нужно беспокоиться о node.js :)

    Плюс по опыту работы с nuxt.js могу сказать, что там есть свои особенности с роутером, хранилищем, подключением сторонних библиотек. Плюс своя система модулей - отдельный модуль axios, proxy и тд.
    Еще надо четко понимать, когда идет клиентский, а когда серверный рендеринг.
    В общем на первоначальном этапе время разработки может прилично увеличиться.
    Ответ написан
    Комментировать
  • Почему Vue.js возвращает __ob__: Observer?

    @karambafe
    Павел Корнилов все правильно написал, метод get у axios асинхронный, возвращает промис.

    Поэтому метод getPosts тоже нужно сделать асинхронным:
    async getPosts(author) {
       try {
         const { data } = await axios.get(`${options.serverUrl}/posts/${author}`);
         return data; 
      } catch(error) {
         return error; // важный момент, что будет возвращать функция при ошибках выполнения запроса на сервер
      }
     },


    Сделал небольшой пример на codeSanbox, файл App.vue.
    В консоли видна разница вывода для текущего метода getPosts и нового getPostsAsync

    p.s.
    Из контекста данного примера, на мой взгляд, метод getPosts достаточно бесполезный - увеличивает уровень абстракции, но не дает полноценное понимание кода.

    Я бы внес запрос к серверу прямо в метод loadPosts:
    async loadPosts () {
      this.loading = true; // вешаем лоадер на момент асинхронного запроса
    
      try {
         const { data } = await axios.get(`${options.serverUrl}/posts/${this.$route.params.id}`);
         this.posts = data;
         this.loading = false;
      } catch(error) {
         // делаем что-то в случае ошибки
         this.loading = false;
      }
    },
    Ответ написан
    Комментировать