Основы Vue.js - Компоненты и события

Внимание

Данный материал является частью цикла статей «Основы Vue.js». Не забудьте посмотреть другие статьи по этой теме :-)

  1. Основы Vue.js - Создание и настройка проекта
  2. Основы Vue.js - Маршрутизация и навигация
  3. Основы Vue.js - HTTP-клиент Axios и переменные окружения
  4. Основы Vue.js - Создание компонентов и отображение данных с сервера
  5. Основы Vue.js - Компоненты и события
  6. Основы Vue.js - Использование ключевого слова ref для ссылок на компоненты
  7. Основы Vue.js - Двустороннее связывание и директива v-model

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

Создание нового компонента

Вы помните компонент OwnerListиз предыдущей части? Мы собираемся создать новый компонент, который будет отображать данные одного владельца внутри тега tr для таблицы OwnerList.

Давайте перейдем к src/components/ owner и создадим компонент OwnerListRow.vue:

<template>
  <tr>
    <td>{{ owner.name }}</td>
    <td>{{ owner.address }}</td>
    <td>{{ owner.dateOfBirth }}</td>
    <td>
      <b-button variant="default">Details</b-button>
    </td>
    <td>
      <b-button variant="success">Update</b-button>
    </td>
    <td>
      <b-button variant="danger">Delete</b-button>
    </td>
  </tr>
</template>
<script>
export default {
  name: 'OwnerListRow',
  props: [ 'owner' ]
};
</script>

В приведенном выше примере мы использовали переменную owner, но где мы ее объявили? Переменная ownerнаходится в массиве props, и это означает, что наш новый компонент ожидает от родительского компонента отправки объекта owner этому компоненту как свойство. Итак, что нам нужно сделать, так это объявить компонент OwnerListRow в компоненте OwnerList, отобразить его вместо старого тега tr и затем отправить объект владельца обратно в компонент OwnerListRow.

<template>
  <div>
    <b-row>
      <b-col
        md="2"
        offset-md="10">
        <a href="#">Create owner</a>
      </b-col>
    </b-row>
    <br>
    <b-row>
      <b-col md="12">
        <div class="table-responsive">
          <table class="table table-striped">
            <thead>
              <tr>
                <th>Owner name</th>
                <th>Owner address</th>
                <th>Date of birth</th>
                <th>Details</th>
                <th>Update</th>
                <th>Delete</th>
              </tr>
            </thead>
            <tbody>
              <owner-list-row
                v-for="owner in owners"
                :key="owner.id"
                :owner="owner"/>
            </tbody>
          </table>
        </div>
      </b-col>
    </b-row>
  </div>
</template>
<script>
import OwnerService from '@/api-services/owner.service';
import OwnerListRow from '@/components/owner/OwnerListRow';

export default {
  name: 'OwnerList',
  components: {
    OwnerListRow
  },
  data() {
    return {
      owners: []
    };
  },
  created() {
    OwnerService.getAll().then((response) => {
      this.owners = response.data;
    });
  }
};
</script>

Давайте проанализируем этот код. Сначала мы импортируем новый компонент OwnerListRow. Затем мы сообщаем Vue.js, что хотим использовать OwnerListRow внутри этого компонента. Мы делаем это, указывая имя компонента внутри объекта JSON components. Прямо сейчас Vue.js собирается создать настраиваемый тег owner-list-row, который будет указывать на компонент OwnerListRow под капотом. В противном случае мы также можем указать собственный тег, изменив его на:

components: {
    'some-custom-tag': OwnerListRow
},

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

Теперь, когда у нас есть специальный тег для нашего компонента, мы собираемся заменить им тег tr. Давайте рассмотрим следующий фрагмент кода:

<tbody>
    <owner-list-row
    v-for="owner in owners"
    :key="owner.id"
    :owner="owner"/>
</tbody>

В этом коде у нас есть директива v-for и свойство :key, которые помогают нам отображать наш компонент owner-list-row для каждого владельца в массиве owner. Но также на каждой итерации мы передаем этого владельца дочернему компоненту с помощью оператора :owner="owner". Это называется привязкой данных, и у нас есть два типа привязки данных. В этом примере мы используем двоеточие :, обозначающее динамическое связывание. Его цель - связать переменные, в данном случае объект-владелец, со всеми его свойствами. Если мы каким-либо образом изменим нашего owner, то дочерний компонент будет уведомлен об этом и получит новое значение. Это называется реактивностью.

Другой тип привязки - без двоеточия, и этот тип привязки связывает только строку. Например, если мы напишем owner = ”owner”, тогда мы передадим только строку owner, а не фактическую переменную owner.

Давайте откроем терминал и введем npm run dev, чтобы проанализировать результаты:

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

Обработка событий

Vue.js может прослушивать собственные события javascript, такие как onclick, onmouseoverи т.д. Мы также можем создавать наши собственные события и прослушивать их. Попробуем разобраться как это сделать прямо сейчас.

Давайте изменим шаблон компонента OwnerList.vue:

<tbody>
    <owner-list-row
    v-for="owner in owners"
    :key="owner.id"
    :owner="owner"
    @click.native="onOwnerClicked(owner.id)"/>
</tbody>

Также давайте создадим метод onOwnerClicked внутри тега скрипта:

<script>
import OwnerService from '@/api-services/owner.service';
import OwnerListRow from '@/components/owner/OwnerListRow';

export default {
  name: 'OwnerList',
  components: {
    OwnerListRow
  },
  data() {
    return {
      owners: []
    };
  },
  created() {
    OwnerService.getAll().then((response) => {
      this.owners = response.data;
    });
  },
  methods: {
    onOwnerClicked(ownerId) {
      console.log(ownerId);
    }
  }
};
</script>

Мы можем прослушивать события с помощью @nameOfEvent, и это событие может находиться как в собственных тегах HTML, так и в пользовательских компонентах. Но есть разница.

Если прослушиватель событий размещен в собственных тегах HTML, то мы слушаем собственные события JavaScript. Например, если мы напишем:

<div @click = "”clickHandler”/">

то мы будем прослушивать событие onClickи вызывать clickHandler из объекта метода в теге скрипта. Но если мы хотим прослушивать собственные события JavaScript в пользовательских компонентах, нам нужно использовать модификатор .native, чтобы указать Vue.js прослушивать собственные события, а не пользовательские события.

Когда мы нажимаем на первую строку, запускается метод onOwnerClicked, и мы получаем id этого владельца в консоли:

Теперь попробуем сгенерировать настраиваемые события для каждой кнопки в нашем компоненте OwnerListRow.vue.

<template>
  <tr>
    <td>{{ owner.name }}</td>
    <td>{{ owner.address }}</td>
    <td>{{ owner.dateOfBirth }}</td>
    <td>
      <b-button
        variant="default"
        @click="onDetailsClick">Details</b-button>
    </td>
    <td>
      <b-button
        variant="success"
        @click="onUpdateClick">Update</b-button>
    </td>
    <td>
      <b-button
        variant="danger"
        @click="onDeleteClick">Delete</b-button>
    </td>
  </tr>
</template>
<script>
export default {
  name: 'OwnerListRow',
  props: [ 'owner' ],
  methods: {
    onDetailsClick() {
      this.$emit('details', this.owner.id);
    },
    onUpdateClick() {
      this.$emit('update', this.owner.id);
    },
    onDeleteClick() {
      this.$emit('delete', this.owner.id);
    }
  }
};
</script>

Как подписаться на событие click в элементе b-button. Элемент b-buttonгенерирует настраиваемое событие с именем click, когда мы нажимаем на эту кнопку. Этим мы и воспользуемся. Мы будем прослушивать событие clickна всех трех кнопках, а затем генерировать новые события с именами details, updateи delete.. Мы также передаем id владельца в качестве параметра этого события.

Теперь давайте послушаем эти события в компоненте OwnerList.vue. Мы собираемся удалить прослушиватель событий @click.native, потому что мы хотим открывать сведения о владельце, когда мы нажимаем кнопку details, а не в строке таблицы.

<tbody>
  <owner-list-row
    v-for="owner in owners"
    :key="owner.id"
    :owner="owner"
    @details="detailsOwner"
    @update="updateOwner"
    @delete="deleteOwner"/>
</tbody>
...
  methods: {
    detailsOwner(ownerId) {
      console.log('details', ownerId);
    },
    updateOwner(ownerId) {
      console.log('update', ownerId);
    },
    deleteOwner(ownerId) {
      console.log('delete', ownerId);
    }
  }

Давайте откроем веб-браузер и понажимаем на кнопки, чтобы просмотреть результаты:

Вроде все работает!

Заключение

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

Прочитав этот пост, вы узнали:

  • Как создавать свойства компонента
  • Как работает привязка данных в Vue.js
  • Как слушать события
  • Как создавать собственные события

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