В данной статье мы собираемся создать дополнительный компонент, а затем показать, как работает связь между родительско-дочерними компонентами.
Создание нового компонента
Вы помните компонент 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 может прослушивать собственные события 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
для отображения сведений об одном владельце, а также для его обновления и удаления.