Основы Vue.js - Двустороннее связывание и директива v-model

Внимание

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

Нам все еще не хватает компонентов создания и обновленияд данных, и в этой статье мы собираемся реализовать эти недостающие части.

Создание объекта-владельца (Owner)

Мы собираемся создать форму с тремя полями ввода: имя, дата рождения и адрес. Как только пользователь заполнит данные и отправит форму, мы отправим на сервер POST запрос. После получения ответа появится модальное окно и отобразится, успешно ли мы создали сущность «Владелец».

Чтобы упростить чтение и понимание, мы собираемся написать этот компонент в нескольких сегментах. Начнем с шаблона.

Шаблон OwnerCreate

Во-первых, мы собираемся создать форму с тремя полями ввода. Для этого давайте создадим компонент OwnerCreate.vueв папке src/components/Owner:

<template>
  <b-container fluid>
    <div class="form-wrapper">
      <b-form @submit.prevent="createOwner">
        <b-form-group 
          :label-cols="2" 
          breakpoint="md" 
          horizontal 
          label="Name of the owner:"
          for="name">
          <b-col :md="5">
            <b-input 
              id="name" 
              v-model="formData.name" 
              maxlength="60" 
              required />
          </b-col>
        </b-form-group>
        <b-form-group
            ​:label-cols="2"
            ​breakpoint="md"
            ​horizontal
            ​label="Date of birth:"
            ​for="dateOfBirth">
            <b-col :md="5">
            <b-input
               id="dateOfBirth"
               ​v-model="formData.dateOfBirth"
               ​type="date"
               ​required />
            </b-col>
        </b-form-group>
        <b-form-group
            ​:label-cols="2"
            ​breakpoint="md"
            ​horizontal
            ​label="Address:"
            ​for="Address">
            <b-col :md="5">
            <b-input
               ​id="Address"
               ​v-model="formData.address"
               maxlength="100"
               ​required />
            </b-col>
        </b-form-group>
       </b-form>
    </div>
   </b-container>
</template>

В этом шаблоне есть кое что новое. В теге формы есть оператор @submit.prevent. Ранее мы узнали, что @submit- это синтаксис для прослушивания события submit. Но теперь у нас есть модификатор prevent, который эквивалентен стандартному выражению JavaScript event.preventDefault(). Таким образом, мы предотвращаем перенаправление на действие формы в собственном браузере, потому что мы хотим отправлять данные с помощью Axios.

Также обратите внимание на конструкцию v-model. Чуть позже мы вернемся к ней.

Под тегом формы мы собираемся создать кнопки Сохранитьи Отменить. Первая отправит форму, а вторая перенаправит на маршрут OwnerList.

Итак, прямо под последним тегом b-form-groupмы вставим этот фрагмент кода:

<template>
  <b-container fluid>
    <div class="form-wrapper">
      <b-form @submit.prevent="updateOwner">
        <b-form-group
          :label-cols="2"
          breakpoint="md"
          ...
</template>

Кроме того, мы собираемся создать модальное окно, чтобы показать информацию, было ли наше действие успешным. Мы собираемся поместить это модальное окно перед закрывающим тегом b-container:

<b-modal
    ref="alertModal"
    :title="alertModalTitle"
    :ok-only="true"
    @ok="onAlertModalOkClick">
    <p class="my-4">{{ alertModalContent }}</p>
</b-modal>

В продолжение добавим несколько стилей:

<style>
.form-wrapper {
    margin-top: 20px;
    min-height: 20px;
    padding: 19px;
    margin-bottom: 20px;
    background-color: #f5f5f5;
    border: 1px solid #e3e3e3;
    border-radius: 4px;
    box-shadow: inset 0 1px 1px rgba(0,0,0,.05);
}
</style>

Далее мы реализуем логику этого шаблона.

Сценарий OwnerCreate

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

Пришло время объяснить директиву v-model и то, как мы используем ее для связывания этих переменных с входными данными.

Это похоже на выражение привязки данных (:prop="value"), которое вы могли видеть в предыдущих частях, но это привязка через v-model является двусторонней. В предыдущих частях мы только передали данные из родительского компонента в дочерний компонент с использованием синтаксиса :prop="value", и если дочерний компонент попытается изменить это значение, появится предупреждение. потому что это запрещено. С помощью директивы v-modelродительский и дочерний компоненты могут изменять это значение, и оба компонента будут уведомлены об этом изменении.

<script>
export default {
  name: 'OwnerCreate',
  data() {
    return {
      formData: {
        name: '',
        dateOfBirth: '',
        address: ''
      },
      alertModalTitle: '',
      alertModalContent: '',
      isSuccessfully: false
    };
  },
};
</script>

Теперь давайте реализуем метод createOwner который отправляет запрос POST и объект formData на сервер. В зависимости от ответа сервера переменной isSuccessfully будет установлено значение true или false. Если действие выполнено успешно, появится модальное окно и объект formDataбудет очищен. Если возникнет какая-либо проблема, модальное окно отобразит сообщение об ошибке.

Давайте реализуем этот метод:

methods: {
    createOwner() {
      OwnerService.create(this.formData)
      .then(() => {
        this.isSuccessfully = true;
        this.alertModalTitle = 'Successfully';
        this.alertModalContent = 'Successfully created Account Owner';
        this.$refs.alertModal.show();
        ​this.formData = {
            ​name: '',
            ​dateOfBirth: '',
            ​address: ''
        ​};
        ​})
       .catch((error) => {
            ​this.isSuccessfully = false;
            ​this.alertModalTitle = 'Error';
            ​this.alertModalContent = error.response.data;
            ​this.$refs.alertModal.show();
        ​});
    }
}

Нам также необходимо импортировать ссылку OwnerService:

import OwnerService from '@/api-services/owner.service';

Когда появится модальное окно, нам нужно нажать кнопку OK. Этот действие вызовет метод onAlertModalOkClick, который перенаправит пользователя на маршрут OwnerList, если ответ будет успешным успешным.

Мы собираемся отредактировать компонент OwnerCreate.vueеще раз, чтобы реализовать этот метод:

onAlertModalOkClick() {
    if (this.isSuccessfully) {
    this.$router.push({ name: 'OwnerList' });
    }
}

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

Переход к компоненту OwnerCreate

Давайте отредактируем файл src/router/index.js:

{
    path: '/owner/list',
    name: 'OwnerList',
    component: OwnerList
},
{
    path: '/owner/create',
    name: 'OwnerCreate',
    component: OwnerCreate
},
{
    path: '/owner/:id',
    name: 'OwnerDetails',
    component: OwnerDetails
},

Важно разместить наш маршрут над маршрутом OwnerDetails. Если мы разместим его ниже, то при доступе к маршруту /owner/createбудет запущен маршрут OwnerDetails, потому что часть маршрута create будет распознаваться как параметр :id.

И наконец, давайте изменим компонент src/components/Owner/OwnerList.vue:

<template>
  <div>
    <b-row>
      <b-col
        md="2"
        offset-md="10">
        <router-link :to="{ name: 'OwnerCreate' }">Create owner</router-link>
      </b-col>
    </b-row>
    ....

Тег <router-link> создаст тег <a>, но не будет инициировать процесс перенаправления при нажатии, он только запустит vue-router для переключения маршрута.

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

Когда мы вводим действительные данные и нажимаем кнопку Сохранить:

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

Форма редактирования почти идентична форме создания. Есть только несколько отличий. У нас есть параметр :idв маршруте, и мы должны получить данные для этого владельца из серверной части и отобразить их в форме. Как только мы нажмем кнопку Сохранить, мы отправим запрос PUT на серверную часть с новыми данными для этого владельца.

Давайте создадим новый компонент OwnerUpdate.vueвнутри каталога src/components/Owner. Единственная разница между этим шаблоном и шаблоном для создания владельца - это имя обработчика событий для формы отправки. В этом случае давайте просто скопируем шаблон из OwnerCreate.vue и вставим его в новый компонент и отредактируем имя обработчика событий:

<template>
  <b-container fluid>
    <div class="form-wrapper">
      <b-form @submit.prevent="updateOwner">
        <b-form-group
          :label-cols="2"
          breakpoint="md"
          ...
</template>

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

Итак, давайте реализуем сценарий для OwnerUpdate.vue:

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

export default {
  name: 'OwnerUpdate',
  data() {
    return {
      formData: {
        name: '',
        dateOfBirth: '',
        address: ''
      },
      alertModalTitle: '',
      alertModalContent: '',
      isSuccessfully: false
    };
  },
  created() {
    OwnerService.get(this.$router.currentRoute.params.id).then((response) => {
      this.formData.name = response.data.name;
      this.formData.dateOfBirth = response.data.dateOfBirth.split('T')[0];
      this.formData.address = response.data.address;
    });
  },
  methods: {
    updateOwner() {
      OwnerService.update(this.$router.currentRoute.params.id, this.formData).then(() => {
        this.isSuccessfully = true;
        this.alertModalTitle = 'Successfully';
        this.alertModalContent = 'Successfully updated Account Owner';
        this.$refs.alertModal.show();
      }).catch((error) => {
        this.isSuccessfully = false;
        this.alertModalTitle = 'Error';
        this.alertModalContent = error.response.data;
        this.$refs.alertModal.show();
      });
    },
    onAlertModalOkClick() {
      if (this.isSuccessfully) {
        this.$router.push({ name: 'OwnerList' });
      }
    }
  }
};
</script>

Также определим маршрут для этого компонента:

{
    path: '/owner/create',
    name: 'OwnerCreate',
    component: OwnerCreate
},
{
    path: '/owner/update/:id',
    name: 'OwnerUpdate',
    component: OwnerUpdate
},
{
    path: '/owner/:id',
    name: 'OwnerDetails',
    component: OwnerDetails
},

И наконец, нам нужно направить пользователя на эту страницу, когда пользователь нажимает кнопку Обновить в компоненте OwnerList:

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

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

Когда мы нажимаем кнопку Обновитьв компоненте OwnerList, мы будем перенаправлены на новый компонент, и форма будет заполнена фактическими данными:

Давайте попробуем изменить наши поля ввода и нажать кнопку Сохранить. Появится модальное окно:

И мы обновили сущность Owner в компоненте OwnerList.

Заключение

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

  • Как работает двусторонняя привязка
  • Как предотвратить поведение событий по умолчанию
  • Способ обработки запроса POST
  • Способ обработки запроса PUT