Сегодня, в шестом уроке курса по Vue, мы поговорим о том, как динамически стилизовать HTML-элементы, привязывая данные к их атрибутам style и привязывая к элементам классы.



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

Цель урока


Первой целью данного урока будет использование цвета, соответствующего вариантам товаров, для настройки свойства background-color элементов <div>, выводящих сведения об этих вариантах. Так как вариантам товара соответствуют цвета green и blue, нам нужно, чтобы один элемент <div> имел бы зелёный фоновый цвет, а второй — синий.

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

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


Вот как выглядит сейчас код, находящийся в 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 @mouseover="updateProduct(variant.variantImage)">
          {{ variant.variantColor }}
        </p>
      </div>

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

      <div class="cart">
        <p>Cart({{ cart }})</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",
            variantImage: "./assets/vmSocks-green.jpg"    
          },
          {
            variantId: 2235,
            variantColor: "blue",
            variantImage: "./assets/vmSocks-blue.jpg" 
          }
        ],
        cart: 0
    },
    methods: {
      addToCart() {
        this.cart += 1
      },
      updateProduct(variantImage) {
        this.image = variantImage
      }
    }
})

Задача


В предыдущем уроке мы создали обработчик событий, который меняет изображение товара, основываясь на том, на какой элемент <p> был наведён указатель мыши. Вместо того чтобы выводить название цвета в элементе <p>, мы хотели бы использовать этот цвет для настройки свойства background-color соответствующего элемента <div>. При таком подходе, вместо того, чтобы наводить мышь на тексты, мы сможем наводить её на цветные квадраты, что приведёт к выводу на странице изображения товара, цвет которого соответствует цвету, показанному в квадрате.

Решение


Для начала — давайте добавим к элементу <div> класс color-box, который задаёт его ширину, высоту и внешний верхний отступ. Так как мы, даже сделав это, продолжаем выводить в элементах <div> слова green и blue, мы можем взять названия цветов, хранящихся в объектах, описывающих варианты товара, и использовать эти названия при привязке стиля к атрибуту style. Вот как это выглядит:

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

Обратите внимание на вторую и пятую строки этого кода. Здесь мы добавляем к элементу <div> класс color-box и привязываем к нему встроенный стиль. Встроенный стиль здесь используется для динамической настройки свойства background-color элементов <div>. Цвет для фона элементов берётся из variant.variantColor.


Стилизованные элементы <div> и выводимые на них надписи

Теперь, когда элемент <div> стилизован с использованием variantColor, нам больше не нужно выводить в нём название цвета. Поэтому мы можем избавиться от тега <p> и переместить конструкцию @mouseover=«updateProduct(variant.variantImage)» в сам элемент <div>.

Вот как будет выглядеть код после внесения в него вышеописанных изменений:

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


Стилизованные элементы <div> без текста

Теперь при наведении мыши на синий квадрат на странице выводится изображение синих носков. А при наведении мыши на зелёный квадрат — изображение зелёных носков. Красота!

Разобравшись с привязкой стилей, поговорим о привязке классов.

Задача


Сейчас в наших данных есть следующее:

inStock: true,

Когда свойство inStock принимает значение false, нам нужно запретить посетителям сайта щёлкать по кнопке Add to Cart, так как на складе нет товара, а значит, его нельзя добавить в корзину. К нашей удаче, существует специальный HTML-атрибут, носящий имя disabled, с помощью которого можно отключить кнопку.

Если вспомнить материал второго урока, то окажется, что мы можем воспользоваться техникой привязки атрибутов для добавления к элементу атрибута disabled тогда, когда inStock равняется false, или, скорее, в случае, когда это значение не является истинным (!inStock). Перепишем код кнопки:

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

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


Отключённая кнопка выглядит так же, как обычная, но щёлкать по ней бессмысленно

Решение


Тут мы поступим, действуя по той же схеме, по которой действовали, привязывая inStock к атрибуту disabled. А именно, будем привязывать класс disabledButton к нашей кнопке в случаях, когда inStock хранит false. При таком подходе, если по кнопке будет бессмысленно щёлкать, то и выглядеть она будет соответственно.

<button
  v-on:click="addToCart"
  :disabled="!inStock"
  :class="{ disabledButton: !inStock }"
>
  Add to cart
</button>


Отключённая кнопка выглядит так, как нужно

Как видите, теперь кнопка становится серой в том случае, если inStock равняется false.

Давайте разберёмся в том, что здесь происходит.

Взгляните на эту строчку:

:class="{ disabledButton: !inStock }"

Здесь мы используем сокращённый вариант записи директивы v-bind (:) для организации привязки данных к атрибуту class кнопки. В фигурных скобках мы определяем присутствие класса disabledButton на основании истинности свойства inStock.

Другими словами, когда товара на складе нет (!inStock), к кнопке добавляется класс disabledButton. Так как этот класс задаёт серый фоновый цвет кнопки, кнопка становится серой.

Замечательно! Только что мы скомбинировали наши новые знания, касающиеся привязки классов, со знаниями о привязке атрибутов, и смогли отключить кнопку и делать её серой в том случае, если inStock равняется false.

Дополнительные сведения


К элементу можно привязывать объект классов или массив классов:

<div :class="classObject"></div>
<div :class="[activeClass, errorClass]"></div>

Практикум


Когда в inStock записано значение false, нужно привязать к тегу <p>, выводящему текст Out of Stock, класс, который добавляет к элементу стиль text-decoration: line-through, перечёркивая текст.

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

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

Итоги


Вот самое важное из того, что мы сегодня изучили:

  • Данные можно привязывать к атрибуту элементов style.
  • Данные можно привязывать к атрибуту элементов class.
  • При организации привязки классов можно пользоваться выражениями, от вычисления которых зависит то, будет ли соответствующий класс привязан к элементу.


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

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