Основы Vue.js - Использование ключевого слова ref для ссылок на компоненты

Внимание

Данный материал является частью цикла статей «Основы 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

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

Создание компонента сведений о владельце

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

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

Давайте перейдем в каталог src/components/owner и создадим файл OwnerDetails.vue:

<template>
  <div>
    <div class="well">
      <div class="row">
        <div class="col-md-3">
          <strong>Owner name:</strong>
        </div>
        <div class="col-md-3">
          {{ owner.name }}
        </div>
      </div>
      <div class="row">
        <div class="col-md-3">
          <strong>Date of birth:</strong>
        </div>
        <div class="col-md-3">
          {{ owner.dateOfBirth }}
        </div>
      </div>
      <div
        v-if="owner.accounts && owner.accounts.length <= 2"
        class="row">
        <div class="col-md-3">
          <strong>Type of user:</strong>
        </div>
        <div class="col-md-3">
          <span class="text-success">Beginner user.</span>
        </div>
      </div>
      <div
        v-else
        class="row">
        <div class="col-md-3">
          <strong>Type of user:</strong>
        </div>
        <div class="col-md-3">
          <span class="text-info">Advanced user.</span>
        </div>
      </div>
    </div>
    <div class="row">
    <div class="col-md-12">
        <div class="table-responsive">
        <table class="table table-striped">
            <thead>
            <tr>
                <th>Account type</th>
                <th>Date created</th>
            </tr>
            </thead>
            <tbody>
            <tr
                v-for="account in owner.accounts"
                :key="account.id">
                <td>{{ account.accountType }}</td>
                <td>{{ account.dateCreated }}</td>
            </tr>
            </tbody>
        </table>
        </div>
    </div>
    </div>
</div>
</template>
<script>
import OwnerService from '@/api-services/owner.service';

export default {
name: 'OwnerDetails',
data() {
    return {
        owner: {}
    };
},
created() {
    OwnerService.getAccounts(this.$router.currentRoute.params.id).then((response) => {
        this.owner = response.data;
    });
    }
};
</script>

Фрагмент кода не требует пояснений, но давайте проанализируем его.

Описание кода компонента

У нас есть шаблон с некоторыми интерполяциями для отображения имени владельца, дня рождения и т. д. Также существует условный рендеринг, который условно отображает Beginner user или Advanced user текст на основе количества учетных записей, связанных с организацией-владельцем. Внутри блока script у нас есть хук жизненного цикла created, в котором мы извлекаем id владельца из строки запроса с this.$router.currentRoute.params.id, а затем получаем данные из серверной части для этого владельца.

Реализация службы API и маршрутизация к компоненту

Теперь давайте создадим метод getAccounts в OwnerService:

...
delete(id) {
    return Axios.delete(`${RESOURCE_NAME}/${id}`);
},
getAccounts(id) {
    return Axios.get(${RESOURCE_NAME}/${id}/account);
}

Нам все еще нужно создать маршрут для этого компонента.

Для этого мы собираемся отредактировать файл src/router/index.js:

{
    path: '/owner/list',
    name: 'OwnerList',
    component: OwnerList
},
{
    path: '/owner/:id',
    name: 'OwnerDetails',
    component: OwnerDetails
},
{
    path: '*',
    name: 'NotFound',
    component: NotFound
}

Мы только что определили новый маршрут, который будет принимать id в качестве параметра в строке запроса. Мы можем получить доступ к параметрам маршрута в наших компонентах через объект this.$router.currentRoute.params.

Наконец, нам нужно перейти к этому компоненту, когда мы нажмем кнопку Details в таблице списка владельцев.

Давайте отредактируем файл src/components/owner/OwnerList.vue:

detailsOwner(ownerId) {
    this.$router.push({ name: 'OwnerDetails', params: { id: ownerId } });
},

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

Все выглядит идеально.

Теперь давайте реализуем функцию Delete.

Удаление объекта-владельца

Когда пользователь нажимает кнопку Delete, должно открываться диалоговое окно подтверждения. Если мы подтвердим действие удаления, на сервер будет отправлен запрос. Сервер может ответить кодом состояния 204 (Нет содержимого), что означает, что мы успешно удалили объект-владелец.

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

Теперь попробуем создать два модальных окна. Одно из них - модальное окно подтверждения, которое имеет статическое содержимое, а второе - модальное окно, информирующее нас об ответе на запрос на удаление. Итак, во втором окне мы собираемся реализовать привязку данных, потому что нам нужно отображать разные сообщения в зависимости от ответа сервера.

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

Давайте перейдем к компоненту OwnerList.vue и реализуем это действие привязки данных:

<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"
                @details="detailsOwner"
                @update="updateOwner"
                @delete="deleteOwner"/>
            </tbody>
          </table>
        </div>
      </b-col>
    </b-row>
    <b-modal
      ref="deleteConfirmModal"
      title="Confirm your action"
      @ok="onDeleteConfirm"
      @hide="onDeleteModalHide">
      <p class="my-4">Are you sure you want to delete this owner?</p>
    </b-modal>

    <b-modal
      ref="alertModal"
      :title="alertModalTitle"
      :ok-only="true">
      <p class="my-4">{{ alertModalContent }}</p>
    </b-modal>
  </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: [],
      selectedOwnerId: null,
      alertModalTitle: '',
      alertModalContent: ''
    };
  },
  created() {
    this.fetchOwners();
  },
  methods: {
    detailsOwner(ownerId) {
      this.$router.push({ name: 'OwnerDetails', params: { id: ownerId } });
    },
    updateOwner(ownerId) {
      console.log('update', ownerId);
    },
    deleteOwner(ownerId) {
      this.selectedOwnerId = ownerId;
      this.$refs.deleteConfirmModal.show();
    },
    fetchOwners() {
      OwnerService.getAll().then((response) => {
        this.owners = response.data;
      });
    },
    onDeleteConfirm() {
      OwnerService.delete(this.selectedOwnerId).then(() => {
        this.alertModalTitle = 'Successfully';
        this.alertModalContent = 'Successfully deleted Account Owner';
        this.$refs.alertModal.show();
        this.fetchOwners();
      }).catch((error) => {
        this.alertModalTitle = 'Error';
        this.alertModalContent = error.response.data;
        this.$refs.alertModal.show();
      });
    },
    onDeleteModalHide() {
      this.selectedOwnerId = null;
    }
  }
};
</script>

Описание кода компонента

Мы переместили логику выборки владельца из обработчика жизненного цикла created в метод с именем fetchOwners. Мы сделали это по причине того, что нам нужно снова получить всех владельцев после успешного удаления одного владельца. Теперь, когда мы переместили логику в метод, мы можем повторно использовать наш код владельца выборки.

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

Давайте посмотрим на конструкцию this.$refs, которую мы используем впрервые

В части шаблона у нас есть два модальных окна. В обоих модальных окнах у нас есть атрибут ref="something". Так же, как мы можем получить элемент в обычном JavaScript с помощью выражения document.getElementById(), мы можем получить ссылку на элемент (или другой компонент vue, в данном случае b-modal) в Vue.js с помощью операторов ref и this.$refs. Мы можем использовать эти ссылки на модальные окна для запуска внутри них метода show(). Остальная часть шаблона не требует пояснений.

Проверка результатов

Давайте снова откроем терминал и запустим команду npm run dev для просмотра результатов.

Если мы попытаемся удалить Anna Bosh, появится модальное окно подтверждения:

Когда мы нажимаем кнопку OK, появляется ошибка, потому что у Anna Bosh есть связанные учетные записи:

Если мы попытаемся удалить Nick Somion, снова появится диалоговое окно подтверждения:

И появляется сообщение successfully, что значит "успешно":

Заключение

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

  • Как обрабатывать ошибки сервера
  • Как ссылаться на элементы или компоненты из шаблона
  • Как создать диалог подтверждения действия