Сегодня, в пятом уроке курса по Vue.js для начинающих, речь пойдёт о том, как обрабатывать события.



> Vue.js для начинающих, урок 1: экземпляр Vue
> Vue.js для начинающих, урок 2: привязка атрибутов
> Vue.js для начинающих, урок 3: условный рендеринг
> Vue.js для начинающих, урок 4: рендеринг списков

Цель урока


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

Вторая цель заключается в том, чтобы при наведении мыши на названия цветов вариантов товара менялось бы изображение товара.

Начальный вариант кода


В файле проекта index.html будет присутствовать следующий код:

<div id="app">
  <div class="product">
    <div class="product-image">
      <img :src="image" />
    </div>

    <div class="product-info">
      <h1>{{ product }}</h1>
      <p v-if="inStock">In stock</p>
      <p v-else>Out of Stock</p>

      <ul>
        <li v-for="detail in details">{{ detail }}</li>
      </ul>
      <div v-for="variant in variants" :key="variant.variantId">
        <p>{{ variant.variantColor }}</p>
      </div>

    </div>
  </div>
</div>

Вот содержимое main.js:

var app = new Vue({
    el: '#app',
    data: {
        product: "Socks",
        image: "./assets/vmSocks-green.jpg",
        inStock: true,
        details: ['80% cotton', '20% polyester', 'Gender-neutral'],
        variants: [
          {
            variantId: 2234,
            variantColor: "green"
          },
          {
            variantId: 2235,
            variantColor: "blue"
          }
        ]
    }
})

Задача


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

Решение


Для начала — добавим, в main.js, в объект data, новое свойство, которое будет символизировать количество товара в корзине:

cart: 0

Теперь, в index.html, добавим элемент <div>, описывающий корзину. В этом элементе будет использован тег <p>, с помощью которого на страницу будет выводиться число, хранящееся в свойстве cart:

<div class="cart">
  <p>Cart({{ cart }})</p>
</div>

Ещё мы создадим в коде index.html кнопку, которая позволяет добавлять товар в корзину:

<button v-on:click="cart += 1">Add to cart</button>

Здесь обратите внимание на то, что для инкрементирования значения, хранящегося в cart, мы используем директиву v-on.


Страница с корзиной и с кнопкой для добавления товара в корзину 

Если теперь нажать на кнопку — количество товара в корзине увеличится на 1.

Как всё это работает?

Давайте разберёмся в представленной здесь конструкции. Использование директивы v-on сообщает Vue о том, что мы хотим прослушивать события, происходящие с кнопкой. Потом идёт двоеточие, после которого указывается то, какое конкретно событие нас интересует. В данном случае это — событие click. В кавычках записано выражение, которое добавляет 1 к значению, хранящемуся в cart. Это происходит при каждом щелчке по кнопке.

Это — простой, но не вполне реалистичный пример. Вместо того, чтобы указывать в кавычках выражение cart += 1, давайте сделаем так, чтобы щелчок по кнопке вызывал бы метод, который будет увеличивать значение, хранящееся в cart. Вот как это выглядит:

<button v-on:click="addToCart">Add to cart</button>

Как видите, здесь addToCart — это имя метода, который будет вызван при возникновении события click. Но сам метод мы пока не объявили, поэтому давайте сделаем это прямо сейчас, оснастив им наш экземпляр Vue.

Тут используется механизм, очень похожий на тот, который мы уже применяем для хранения данных. А именно, речь идёт о том, что у объекта с опциями, используемого при создании экземпляра Vue, может быть необязательное свойство, носящее имя methods, в котором содержится объект с методами. В нашем случае это будет всего один метод — addToCart:

methods: {
  addToCart() {
    this.cart += 1
  }
}

Теперь, когда мы щёлкаем по кнопке, вызывается метод addToCart, который и увеличивает значение cart, выводящееся в теге <p>.

Продолжим разбор того, что здесь происходит.

Кнопка прослушивает события click благодаря директиве v-on, которая вызывает метод addToCart. Этот метод находится в свойстве methods экземпляра Vue. В теле функции содержится инструкция, добавляющая 1 к значению this.cart. Так как this хранит ссылку на то место, где хранятся данные экземпляра Vue, в котором мы находимся, функция добавляет 1 к значению cart. А this.cart — это то же самое, что и свойство cart, объявленное в свойстве data объекта с опциями.

Если бы мы просто написали бы в теле функции что-то вроде cart += 1, то мы столкнулись бы с сообщением об ошибке cart is not defined. Именно поэтому мы используем конструкцию this.cart и обращаемся к cart из экземпляра Vue, используя this.

Возможно, вы сейчас задаётесь вопросом о том, что сейчас мы просто увеличиваем количество товаров в корзине, но самого товара в корзину не добавляем. Может, мы что-то делаем не так? Это — правильный вопрос. Мы реализуем соответствующий функционал позже, в одном из следующих уроков.

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

Для начала — давайте расширим объекты массива variants из объекта data, добавив туда свойство variantImage, хранящее путь к изображению нужного варианта товара. Приведём соответствующий раздел файла main.js к такому виду:

variants: [
  {
    variantId: 2234,
    variantColor: "green",
    variantImage: "./assets/vmSocks-green.jpg"    
  },
  {
    variantId: 2235,
    variantColor: "blue",
    variantImage: "./assets/vmSocks-blue.jpg" 
  }
],

Теперь каждому варианту товара, зелёным и синим носкам, назначено собственное изображение.

Задача


Нужно, чтобы, по наведению мыши на название цвета варианта носков, в поле, где выводится изображение товара, вывелось бы изображение variantImage для соответствующего цвета.

Решение


Тут нам снова пригодится директива v-on. Но в этот раз мы воспользуемся сокращённым вариантом её записи, который выглядит как @. А прослушивать будем событие mouseover.

Вот соответствующий код в index.html:

<div v-for="variant in variants" :key="variant.variantId">
  <p @mouseover="updateProduct(variant.variantImage)">
    {{ variant.variantColor }}
  </p>
</div>

Обратите внимание на то, что мы передаём методу updateProduct, в виде аргумента, variant.variantImage.

Создадим этот метод в main.js:

updateProduct(variantImage) {
  this.image = variantImage
}

Этот метод очень похож на тот, который мы недавно создавали для увеличения значения cart.

Но тут мы обновляем значение, хранящееся в image. А именно, в image записывается то, что хранится в variantImage того варианта товара, на который наведён указатель мыши. Соответствующее значение передаётся функции updateProduct из самого обработчика события, находящегося в index.html:

<p @mouseover="updateProduct(variant.variantImage)">

Другими словами, теперь метод updateProduct готов к вызову с параметром variantImage.

Когда вызывается этот метод, variant.variantImage передаётся ему в виде variantImage и используется для обновления значения, хранящегося в this.image. Мы, по аналогии с ранее рассмотренной конструкцией this.cart, можем сказать, что this.image — это то же самое, что image. В результате значение, хранящееся в image, теперь динамически обновляется в соответствии с данными варианта товара, на который наведён указатель мыши.

Синтаксис ES6


Здесь мы, создавая методы, пользовались такими конструкциями:

updateProduct(variantImage) {
  this.image = variantImage
}

Это сокращённый вариант описания методов, который появился в ES6. Более старый вариант записи подобных конструкций выглядит так:

updateProduct: function(variantImage) {
  this.image = variantImage
}

Практикум


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

> Вот заготовка, которую вы можете использовать для решения этой задачи.

Вот решение задачи.

Итоги


Подведём итоги сегодняшнего занятия:

  • Для организации реакции элемента на события используется директива v-on.
  • Сокращённый вариант директивы v-on выглядит как @.
  • При использовании v-on можно указать тип прослушиваемого события:

    • click
    • mouseover
    • любое событие DOM
  • Директива v-on может вызывать методы.
  • Метод, вызываемый с помощью v-on, может принимать аргументы.
  • Ключевое слово this содержит ссылку на то место, где хранятся данные текущего экземпляра Vue. Его использование позволяет работать с данными экземпляра, а так же с методами, объявленными в экземпляре.

Получилось ли у вас домашнее задание к этому уроку?

> Vue.js для начинающих, урок 1: экземпляр Vue
> Vue.js для начинающих, урок 2: привязка атрибутов
> Vue.js для начинающих, урок 3: условный рендеринг
> Vue.js для начинающих, урок 4: рендеринг списков