Урок 11 :: Forms & v-model :: Формы и v-model

Вот, что мы с Вами сделаем на этом уроке.
Vue Mastery
Пройдем по этой ссылке:
Forms & v-model ➳

... Все внимательно послушаем, посмотрим, прочитаем.

Затем сделаем конспект (мой см ниже).

Конспект

Формы и v-model (Forms & v-model)
В этом уроке мы рассмотрим концепцию форм и v-model.

Чтобы начать этот урок, вы можете либо получить программой git коммандой checkout исходный код в ветке
L11-start репозитория, либо перейти в CodePen, чтобы взять файлы там.

Примечание переводчика

Чтобы перейти на ветку с файлами начала урока 11 выполните команду:

git checkout L11-start

Наша Цель
Создать форму для пользователей, чтобы они могли добавлять отзывы о товарах.

Представляем v-образную модель (v-model)
В начале этого курса мы узнали о v-привязке (v-bind), которая создает одностороннюю привязку данных к шаблону. Однако при работе с формами этой односторонней привязки недостаточно. Нам также необходимо привязать шаблон к данным (data).

Например, когда пользователь вводит свое имя в поле ввода, мы хотим записать и сохранить это значение в наших данных. Директива v-модели (v-model) помогает нам достичь этого, создавая двустороннюю привязку данных.
les11_pic01.jpg
Чтобы увидеть все это в действии, мы создадим новый компонент формы обзора (review-form).

Компонент формы обзора (review-form)
Мы добавим новый ReviewForm.js файл в нашу папку "Компоненты" и создадим компонент (review-form).

📄 components/ReviewForm.js
app.component('review-form', {
  template:
  /*html*/
  `<form class="review-form">
    <h3>Leave a review</h3>
    <label for="name">Name:</label>
    <input id="name">

    <label for="review">Review:</label>      
    <textarea id="review"></textarea>

    <label for="rating">Rating:</label>
    <select id="rating">
      <option>5</option>
      <option>4</option>
      <option>3</option>
      <option>2</option>
      <option>1</option>
    </select>

    <input class="button" type="submit" value="Submit">
  </form>`,
  data() {
    return {
      name: '',
      review: '',
      rating: null
    }
  }
})

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

📄 main.js
  data() {
    return {
      name: '',
      review: '',
      rating: null
    }
  }

Мы достигнем этого, добавив директиву v-model к каждому из этих вводимых (input) элементов.

📄 components/ReviewForm.js
app.component('review-form', {
  template:
  /*html*/
  `<form class="review-form">
    <h3>Leave a review</h3>
    <label for="name">Name:</label>
    <input id="name" v-model="name">

    <label for="review">Review:</label>      
    <textarea id="review" v-model="review"></textarea>

    <label for="rating">Rating:</label>
    <select id="rating" v-model.number="rating">
      <option>5</option>
      <option>4</option>
      <option>3</option>
      <option>2</option>
      <option>1</option>
    </select>

    <input class="button" type="submit" value="Submit">  
  </form>`,
  data() {
    return {
      name: '',
      review: '',
      rating: null
  }
})

Обратите внимание, как в элементе <select> мы использовали v-model.number, это модификатор, который вводит значение в виде числа.

Отправка формы отзыва (Review Form)
Чтобы отправить эту форму, мы добавим слушателя вверху:

📄 components/ReviewForm.js
app.component('review-form', {
  template:
  /*html*/
  `<form class="review-form" @submit.prevent="onSubmit">
    ...
    <input class="button" type="submit" value="Submit">  
  </form>`
  ...
})

Мы используем другой модификатор @submit.prevent="onSubmit", чтобы предотвратить поведение по умолчанию (обновление браузера). Когда эта форма будет отправлена, она вызовет метод onSubmit(), который мы сейчас напишем:

📄 components/ReviewForm.js
...
data() {
  return {
    name: '',
    review: '',
    rating: null
   }
 },
 methods: {
   onSubmit() {
     let productReview = {
       name: this.name,
       review: this.review,
       rating: this.rating,
     }
     this.$emit('review-submitted', productReview)

     this.name = ''
     this.review = ''
     this.rating = null
   }
 }
...

Этот метод создаст объект обзора продукта (productReview), содержащий название (name), обзор (review) и рейтинг (rating) на основе наших данных (data). Затем он выдаст ($emit) событие (review-submitted), отправив этот обзор продукта (productReview) в качестве полезной нагрузки.

Наконец, мы очищаем поля данных.

Использование формы отзыва (Review Form)
Теперь, когда наша форма обзора создана, мы можем импортировать ее в index.html.

📄 index.html
<!-- Import Components -->
...
<script src="./components/ReviewForm.js"></script>
...

Затем мы перейдем к отображению продукта (product-display) и фактически используем компонент в его шаблоне под “контейнером продукта" (“product-container”).

📄 components/ProductDisplay.js
template: 
  /*html*/
  `<div class="product-display">
    <div class="product-container">
     ...
    </div>
    <review-form></review-form>
  </div>`
})

Теперь в браузере мы можем увидеть форму обзора.
les11_pic02.jpg
Похоже, это работает... за исключением того, что, когда мы нажимаем кнопку "Отправить" ("Submit"), мы отправляем событие, но мы нигде его не прослушивали. Как мы узнали на предыдущем уроке, нам нужно прослушивать событие (review-submitted), отправленное для проверки, в родительской области (в product-display).

Когда событие “услышано”, мы добавим полезную нагрузку productReview в данные (data) компонента отображения продукта (product-display).

Добавление отзывов о товарах
Мы добавим прослушиватель событий здесь, в форме обзора (review-form), где он используется:

📄 components/ProductDisplay.js
template: 
  /*html*/
  `<div class="product-display">
    <div class="product-container">
     ...
    </div>
    <review-form @review-submitted="addReview"></review-form>
  </div>`
})

Когда произойдет событие, мы запустим новый метод addReview(). Это добавит отзывы о продукте в наш компонент отображения продукта (product-display), что означает, что компоненту требуется новый массив отзывов (reviews) в своих данных (data).

📄 components/ProductDisplay.js
...
data() {
  return {
    ...
    reviews: []
  }
}
...

Теперь давайте конкретизируем метод addReview():

📄 components/ProductDisplay.js
...
data() {
  return {
    ...
    reviews: []
   }
 },
methods: {
  ...
  addReview(review) {
    this.reviews.push(review)
  }
},
...

Как вы можете видеть, он принимает отзыв (review), который мы получили из полезной нагрузки события, отправленного на проверку (review-submitted), и помещает его в массив отзывов (reviews).

Отображение отзывов
Теперь, когда мы внедрили возможность добавлять отзывы, нам нужно отображать эти отзывы. Давайте создадим для этого новый компонент. Этот компонент будет называться обзорным списком (review-list), который мы сформируем следующим образом:

📄 components/ReviewList.js
app.component('review-list', {
  props: {
    reviews: {
      type: Array,
      required: true
    }
  },
  template:
  /*html*/
  `
  <div class="review-container">
  <h3>Reviews:</h3>
    <ul>
      <li v-for="(review, index) in reviews" :key="index">
        {{ review.name }} gave this {{ review.rating }} stars
        <br/>
        "{{ review.review }}"
        <br/>
      </li>
    </ul>
  </div>
`
})

У него будет проп, чтобы он мог получать отзывы (reviews) и распечатывать их в шаблоне с помощью v-for, включая индекс (index), чтобы мы могли привязать к нему атрибут :key.

Теперь мы можем импортировать этот компонент внутрь index.html:

📄 index.html
<!-- Import Components -->
...
<script src="./components/ReviewList.js"></script>
...

Затем добавьте его в меню продукта (product-display), прямо над формой обзора (review-form):

📄 components/ProductDisplay.js
template: 
  /*html*/
  `<div class="product-display">
    <div class="product-container">
     ...
    </div>
    <review-list :reviews="reviews"></review-list>
    <review-form @review submitted="addReview"></review-form>
  </div>`
})

Обратите внимание, как мы добавили :reviews="reviews", чтобы мы могли передавать отзывы (reviews), которые живут в product-display, в списке отзывов (review-list).

Проверив это в браузере, мы добавим новый отзыв, нажмем "Отправить" ("Submit") и увидим, что отзыв отображается.
les11_pic03.jpg
Пока все хорошо, но когда мы обновляем (и отзывов нет), мы все равно видим пустое поле, потому что компонент списка отзывов (review-list) все еще отображается без отзывов. Давайте исправим это и отрисоваем этот компонент только тогда, когда у нас есть отзывы (reviews) для отображения.

📄 components/ProductDisplay.js
template: 
  /*html*/
  `<div class="product-display">
    ...
    <review-list v-if="reviews.length" :reviews="reviews"></review-list>
    ...
  </div>`
})

Другими словами, если массив отзывов (reviews) пуст, мы не будем показывать компонент списка отзывов (review-list).

При обновлении кажется, что все работает ок, и компонент отображается только после того, как мы добавили отзыв.

Проверка Базовой Формы
Чтобы закончить этот урок, мы добавим некоторые очень простые проверки в нашу форму обзора (review-form).

📄 components/ReviewForm.js
methods: {
  onSubmit() {
    if (this.name === '' || this.review === '' || this.rating === null) {
      alert('Review is incomplete. Please fill out every field.')
      return
    }
  ...
  }
}

Прежде чем мы создадим productReview, мы проверим, пусты ли this.name, this.review, this.rating. Если хотя бы одно из этих свойств пустое, мы покажем предупреждение, в котором говорится: ‘Обзор неполон. Пожалуйста, заполните все поля.’ Затем будет выполнен выход из метода.

Это чрезмерно упрощенный метод проверки формы. Если вы заинтересованы в изучении дополнительных методов проверки форм производственного уровня, ознакомьтесь с нашими уроками по Vuelidate в нашем курсе Vue следующего уровня: Next-Level Vue

Поздравляю!
Вы дошли до конца курса "Введение в Vue 3" и сделали свой первый шаг на пути к овладению Vue. Чтобы продолжить обучение, я приглашаю вас ознакомиться с нашей библиотекой Vue.js курсы ( library of Vue.js courses), которые охватывают все темы, необходимые вам для успеха в качестве разработчика Vue.

Если вы ищете более управляемый и практический опыт обучения, мы рекомендуем ознакомиться с этим обучением Vue нашего партнера Джеффри Байлса (Vue Training), который будет вашим наставником в течение 8-недельного обучения.

Задача кодирования
Мы подошли к концу этого урока, теперь будем решать новые задачи кодирования.

Вот, что надо сделать.

Добавьте вопрос в форму review-form: 'Would you recommend this product?' ('Будете ли Вы рекомендовать этот товар?')

Запишите и отправьте ответ, а затем отобразите его в списке отзывов (review-list).

Вы можете найти код решения, загрузив L11-end ветвь репозитория или просмотрев решение в Codepen.

Чтобы перейти на ветку с файлами окончания урока 11 выполните команду:

git checkout L11-end

Код файла index.html данного урока

    <div id="app">
      <div class="nav-bar"></div>

      <div class="cart">Cart({{ cart.length }})</div>
      <product-display :premium="premium" @add-to-cart="updateCart"></product-display>
    </div>

    <!-- Import App -->
    <script src="./main.js"></script>

    <!-- Import Components -->
    <script src="./components/ProductDisplay.js"></script>
    <script src="./components/ReviewForm.js"></script>
    <script src="./components/ReviewList.js"></script>

    <!-- Mount App -->
    <script>
      const mountedApp = app.mount('#app')
    </script


Код файла main.js данного урока

См здесь: Код файла main.js данного урока



Код файла ProductDisplay.js данного урока

См здесь: Код файла ProductDisplay.js данного урока



Код файла ReviewForm.js данного урока

См здесь: Код файла ReviewForm.js данного урока



Результат выполнения данного урока

Cart({{ cart.length }})


Примечание переводчика

1. Я слегка меняю контент оригинальных файлов курса для тренировки, проверки русификации и т.д.

2. Код для HTML я размещаю в этом файле между тегами body.

3. Обратите внимание, чтобы в консоле браузера не было ошибок.

Повторенье - мать учения!

Теперь Нажмите здесь для перехода к первому уроку ➳

... или ...


Ссылки по теме.