Angular - Работа с запросами PUT

Внимание

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

  1. Angular - Подготовка проекта для нового приложения
  2. Angular - навигация и маршрутизация
  3. Angular - HttpClient, сервисы и файлы окружения
  4. Angular - Ленивая загрузка данных (lazy loading)
  5. Angular - обработка ошибок
  6. Angular - декораторы и директивы @Input и @Output
  7. Angular - валидация формы и POST запрос
  8. Angular - Работа с запросами PUT
  9. Angular - Работа с DELETE запросами

Структура и маршрутизация папок

Перед любым действием обновления нам необходимо создать файлы наших компонентов.

Итак, давайте создадим их с помощью команды Angular CLI, которая создаст все файлы и импортирует созданный компонент OwnerUpdate в файл owner.module.ts:

ng g component owner/owner-update --skipTests

Нам нужно изменить файл owner.module.ts:

RouterModule.forChild([
      { path: 'list', component: OwnerListComponent },
      { path: 'details/:id', component: OwnerDetailsComponent },
      { path: 'create', component: OwnerCreateComponent },
      { path: 'update/:id', component: OwnerUpdateComponent }

Теперь мы собираемся изменить наши файлы owner-list.component.html и owner-list.component.ts, чтобы включить навигацию между компонентами OwnerList и OwnerUpdate:

<button type="button" id="update" class="btn btn-success" (click)="redirectToUpdatePage(owner.id)">Update</button>
public redirectToUpdatePage = (id) => { 
    const updateUrl: string = `/owner/update/${id}`; 
    this.router.navigate([updateUrl]); 
}

Обработка действия PUT в файле HTML с помощью Angular

Наш файл owner-update.component.html будет почти таким же, как HTML-файл для создания владельца. В таком случае давайте начнем с реализации.

Во-первых, давайте добавим код оболочки в файл owner-update.component.html:

<div class="container-fluid">
    <form [formGroup]="ownerForm" autocomplete="off" novalidate (ngSubmit)="updateOwner(ownerForm.value)">
        <div class="form-horizontal card card-body bg-light mb-2 mt-2">

        </div>
    </form>
</div>

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

Во-вторых, мы собираемся создать наши элементы управления между тегом div form-horizontal:

<div class="form-group">
  <label for="name" class="control-label col-md-2">Name of owner: </label>
  <div class="col-md-5">
   <input type="text" formControlName="name" id="name" class="form-control" />
  </div>
  <div class="col-md-5">
   <em *ngIf="validateControl('name') && hasError('name', 'required')">Name is required</em>
    <em *ngIf="validateControl('name') && hasError('name', 'maxlength')">Maximum allowed length is 60 characters.</em>
  </div>
</div>

<div class="form-group">
  <label for="dateOfBirth" class="control-label col-md-2">Date of birth: </label>
  <div class="col-md-5">
    <input type="text" formControlName="dateOfBirth" id="dateOfBirth" class="form-control" appDatepicker 
          (change)="executeDatePicker($event)" readonly />
  </div>
  <div class="col-md-5">
    <em *ngIf="validateControl('dateOfBirth') && hasError('dateOfBirth', 'required')">Date of birth is required</em>
  </div>
</div>

<div class="form-group">
  <label for="address" class="control-label col-md-2">Address: </label>
  <div class="col-md-5">
    <input type="text" formControlName="address" id="address" class="form-control" />
  </div>
  <div class="col-md-5">
    <em *ngIf="validateControl('address') && hasError('address', 'required')">Address is required</em>
    <em *ngIf="validateControl('address') && hasError('address', 'maxlength')">Maximum allowed length is 100 characters.</em>
  </div>
</div>

Каждый входной элемент содержит атрибут formControlName, который мы собираемся использовать в файле компонента для проверки. Кроме того, функции validateControl и hasError - это настраиваемые функции, которые помогут нам отображать сообщения об ошибках (по-прежнему то же самое, что мы делали в компоненте CreateOwner).

Под последним тегом form-group мы собираемся добавить код для отображения кнопок:

<br><br>
<div class="form-group">
    <div class="col-md-offset-5 col-md-1">
        <button type="submit" class="btn btn-info" [disabled]="!ownerForm.valid">Update</button>
    </div>
    <div class="col-md-1">
       <button type="button" class="btn btn-danger" (click)="redirectToOwnerList()">Cancel</button>
    </div>
</div>

Наконец, под тегом формы давайте добавим наши настраиваемые модальные компоненты:

  <app-success-modal [modalHeaderText]="'Success message'" 
  [modalBodyText]="'Action completed successfully'" 
  [okButtonText]="'OK'" 
  (redirectOnOK)="redirectToOwnerList()"></app-success-modal>

  <app-error-modal [modalHeaderText]="'Error message'" 
  [modalBodyText]="errorMessage" 
  [okButtonText]="'OK'"></app-error-modal>

Теперь у нас есть HTML-файл, и пора реализовать бизнес-логику для файла owner-update.component.

Обработка действия PUT в компоненте Angular

Измените файл owner-update.component.ts:

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { ErrorHandlerService } from './../../shared/services/error-handler.service';
import { RepositoryService } from './../../shared/services/repository.service';
import { Router, ActivatedRoute } from '@angular/router';
import { Owner } from './../../_interfaces/owner.model';
import { DatePipe } from '@angular/common';

@Component({
  selector: 'app-owner-update',
  templateUrl: './owner-update.component.html',
  styleUrls: ['./owner-update.component.css']
})
export class OwnerUpdateComponent implements OnInit {

  public errorMessage: string = '';
  public owner: Owner;
  public ownerForm: FormGroup;

  constructor(private repository: RepositoryService, private errorHandler: ErrorHandlerService, private router: Router,
    private activeRoute: ActivatedRoute, private datePipe: DatePipe) { }

}

Это базовая установка для этого компонента. Мы создаем наши свойства и внедряем все необходимые нам сервисы в этот компонент.

Давайте добавим этот код под конструктором:

ngOnInit() {
  this.ownerForm = new FormGroup({
    name: new FormControl('', [Validators.required, Validators.maxLength(60)]),
    dateOfBirth: new FormControl('', [Validators.required]),
    address: new FormControl('', [Validators.required, Validators.maxLength(100)])
  });

  this.getOwnerById();
}

private getOwnerById = () => {
  let ownerId: string = this.activeRoute.snapshot.params['id'];
    
  let ownerByIdUrl: string = `api/owner/${ownerId}`;

  this.repository.getData(ownerByIdUrl)
    .subscribe(res => {
      this.owner = res as Owner;
      this.ownerForm.patchValue(this.owner);
      $('#dateOfBirth').val(this.datePipe.transform(this.owner.dateOfBirth, 'MM/dd/yyyy'));
    },
    (error) => {
      this.errorHandler.handleError(error);
      this.errorMessage = this.errorHandler.errorMessage;
    })
}

В функции ngOnInit мы создаем экземпляр ownerForm со всеми элементами управления формы и добавляем правила проверки. Затем мы вызываем функцию getOwnerById, чтобы получить с сервера владельца с точным идентификатором.

Внутри этой функции мы выполняем знакомые действия. Извлечение идентификатора из URL-адреса, отправка запроса PUT и обработка ответа, независимо от того, является ли он успешным или ошибочным.

Следует обратить внимание на преобразование значения dateOfBirth из формата с часами, минутами и секундами в формат, который мы ожидаем в нашем элементе управления вводом. С помощью функции JQuery мы помещаем это значение в поле ввода. Мы используем DatePipe для преобразования формата даты.

Давайте добавим дополнительные функции под функцией getOwnerById:

public validateControl = (controlName: string) => {
  if (this.ownerForm.controls[controlName].invalid && this.ownerForm.controls[controlName].touched)
    return true;

  return false;
}

public hasError = (controlName: string, errorName: string)  => {
  if (this.ownerForm.controls[controlName].hasError(errorName))
    return true;

  return false;
}

public executeDatePicker = (event) => {
  this.ownerForm.patchValue({ 'dateOfBirth': event });
}

public redirectToOwnerList = () => {
  this.router.navigate(['/owner/list']);
}

Это знакомые функции для проверки полей ввода, исправления значения в свойстве ownerForm и перенаправления на компонент OwnerList.

Наконец, нам нужно выполнить действие обновления, добавив код прямо под функцией redirectToOwnerList:

public updateOwner = (ownerFormValue) => {
  if (this.ownerForm.valid) {
    this.executeOwnerUpdate(ownerFormValue);
  }
}

private executeOwnerUpdate = (ownerFormValue) => {
 
  this.owner.name = ownerFormValue.name;
  this.owner.dateOfBirth = ownerFormValue.dateOfBirth;
  this.owner.address = ownerFormValue.address;

  let apiUrl = `api/owner/${this.owner.id}`;
  this.repository.update(apiUrl, this.owner)
    .subscribe(res => {
      $('#successModal').modal();
    },
    (error => {
      this.errorHandler.handleError(error);
      this.errorMessage = this.errorHandler.errorMessage;
    })
  )
}

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

Заключение

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

  • Как создать HTML-часть действия для обновления данных
  • Способ внесения значений с сервера в группу форм Angular
  • Как отправить запрос PUT на сервер

Спасибо, что прочитали этот пост, надеюсь, он был вам полезен.

В следующей статье мы собираемся написать как обработать напрос на удаление в Angular