در این پست، ما به شما نشان می دهیم که چگونه درخواست های PUT را با Angular هنگام update کردن منابع توسط برنامه NET Core Web API server. مدیریت کنید.
بدون مقدمه، شروع میکنیم.
اگر می خواهید تمام آموزشهای لازم و پایه مربوط به آموزش گام به گام Angular را ببینید ، لطفاً روی این لینک کلیک کنید: مقدمه آموزش گام به گام Angular.
ساختار پوشه و Routing
قبل از هر اقدام به روز رسانی، باید فایل های component خود را ایجاد کنیم.
بنابراین، با استفاده از دستور Angular CLI آنها را ایجاد میکنیم که این دستور تمام فایلها را ایجاد میکند و کامپوننت OwnerUpdate ایجاد شده را در فایل owner.module.ts ایمپورت میکنیم:
1 |
ng g component owner/owner-update --skip-tests |
اکنون، برای ایجاد route به این component، باید آرایه routes را در فایل owner routing تغییر دهیم:
1 2 3 4 5 6 |
const routes: Routes = [ { 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 خود را تغییر دهیم تا navigation بین component های OwnerList و OwnerUpdate برقرار شود:
1 2 |
<td><button type="button" id="update" class="btn btn-success" (click)="redirectToUpdatePage(owner.id)">Update</button></td> |
و در فایل ts. :
1 2 3 4 |
public redirectToUpdatePage = (id) => { const updateUrl: string = `/owner/update/${id}`; this.router.navigate([updateUrl]); } |
در این مرحله، ما rout خود را تعریف کرده ایم، و می توانیم برای handle کردن درخواست PUT به مرحله بعد برویم.
ایجاد Form برای Handle کردن درخواستهای PUT با Angular
فایل owner-update.component.html ما تقریباً مشابه فایل HTML برای ایجاد owner خواهد بود. از آنجایی که این چنین است، اجازه دهید پیاده سازی آن را شروع کنیم.
ابتدا، اجازه دهید کد wrappers را در فایل owner-update.component.html به این صورت اضافه کنیم:
1 2 3 4 5 6 7 |
<div class="container-fluid"> <form [formGroup]="ownerForm" autocomplete="off" novalidate (ngSubmit)="updateOwner(ownerForm.value)"> <div class="card card-body bg-light mb-2 mt-2"> </div> </form> </div> |
ما از پست قبلی می دانیم که formGroup قرار است تمام کنترل های داخل مقدار خود را در بر داشته باشد. مقدار آن دقیقاً همان چیزی است که به عنوان پارامتر به اکشن updateOwner ارسال می کنیم.
ثانیا، ما میخواهیم کنترلهای خود را بین تگ div card card-body ایجاد کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<div class="row mb-3"> <label for="name" class="col-form-label col-md-2">Name of the 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="mb-3 row"> <label for="dateOfBirth" class="col-form-label col-md-2">Date of birth: </label> <div class="col-md-5"> <input type="text" formControlName="dateOfBirth" id="dateOfBirth" class="form-control" readonly bsDatepicker/> </div> <div class="col-md-5"> <em *ngIf="validateControl('dateOfBirth') && hasError('dateOfBirth', 'required')">Date of birth is required</em> </div> </div> <div class="mb-3 row"> <label for="address" class="col-form-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> |
اینجا هر عنصر input حاوی یک ویژگی formControlName است که میخواهیم از آن در فایل component برای اعتبارسنجی استفاده کنیم. علاوه بر این، توابع validateControl و hasError توابع سفارشی هستند که به ما کمک می کنند پیام های خطا را نمایش دهیم (همان کاری که در کامپوننت CreateOwner انجام دادیم).
زیر آخرین عنصر <“div class=”mb-3 row>، می خواهیم دکمه ها را اضافه کنیم:
1 2 3 4 5 6 7 8 9 10 |
<br><br> <div class="mb-3 row"> <div class="offset-5 col-md-1"> <button type="submit" class="btn btn-info" [disabled]="!ownerForm.valid">Save</button> </div> <div class="col-md-1"> <button type="button" class="btn btn-danger" (click)="redirectToOwnerList()">Cancel</button> </div> </div> |
اکنون فایل HTML خود را در اختیار داریم و زمان آن رسیده است که business logic برای فایل owner-update.component را پیاده سازی کنیم.
Business Logic در فایل Component برای مدیریت درخواستهای PUT با Angular
با تمام import ها داخل فایل owner-update.component.ts شروع میکنیم:
1 2 3 4 5 6 7 8 9 10 11 |
import { Component, OnInit } from '@angular/core'; import { OwnerForUpdate } from './../../_interfaces/ownerForUpdate.model'; import { HttpErrorResponse } from '@angular/common/http'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { Owner } from './../../_interfaces/owner.model'; import { OwnerRepositoryService } from 'src/app/shared/services/owner-repository.service'; import { ErrorHandlerService } from 'src/app/shared/services/error-handler.service'; import { ActivatedRoute, Router } from '@angular/router'; import { DatePipe } from '@angular/common'; import { ModalOptions, BsModalRef, BsModalService } from 'ngx-bootstrap/modal'; import { SuccessModalComponent } from 'src/app/shared/modals/success-modal/success-modal.component'; |
در مرحله بعد، ویژگی های مورد نیاز را اضافه کرده و service های مورد نیاز را inject میکنیم:
1 2 3 4 5 6 7 |
owner: Owner; ownerForm: FormGroup; bsModalRef?:BsModalRef; constructor(private repository: OwnerRepositoryService, private errorHandler: ErrorHandlerService, private router: Router, private activeRoute: ActivatedRoute, private datePipe: DatePipe, private modal: BsModalService) { } |
در ادامه، باید تابع ngOnInit را تغییر داده و یک تابع دیگر را اضافه کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
ngOnInit(): void { 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 = () => { const ownerId: string = this.activeRoute.snapshot.params['id']; const ownerByIdUri: string = `api/owner/${ownerId}`; this.repository.getOwner(ownerByIdUri) .subscribe({ next: (own: Owner) => { this.owner = { ...own, dateOfBirth: new Date(this.datePipe.transform(own.dateOfBirth, 'MM/dd/yyyy')) }; this.ownerForm.patchValue(this.owner); }, error: (err: HttpErrorResponse) => this.errorHandler.handleError(err) }) } |
در تابع ngOnInit، ما ownerForm
را با تمام کنترلهای form نمونهسازی کرده و قوانین اعتبارسنجی را به آن اضافه میکنیم. سپس تابع getOwnerById را فراخوانی کرده تا owner را با id مورد نظر از سرور fetch کنیم.
در داخل این تابع، اقداماتی که برای ما آشنا هستند را اجرا می کنیم. id
را از URI بیرون میکشیم و رشته API URI را ایجاد میکنیم، درخواست GET را ارسال کرده و response را چه success باشد یا یک error پردازش میکنیم.
یکی از مواردی که باید به آن توجه کرد تبدیل مقدار dateOfBirth به قالبی است که در کنترل ورودی خود انتظار داریم آن را نمایش دهیم. ما اینجا از DatePipe برای تبدیل فرمت استفاده می کنیم.
اکنون باید توابع اعتبارسنجی خطا را اضافه کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
validateControl = (controlName: string) => { if (this.ownerForm.get(controlName).invalid && this.ownerForm.get(controlName).touched) return true; return false; } hasError = (controlName: string, errorName: string) => { if (this.ownerForm.get(controlName).hasError(errorName)) return true; return false; } |
اینها توابعی آشنا برای اعتبار سنجی فیلدهای ورودی هستند.
اکنون، قبل از ارسال درخواست PUT، میخواهیم یک interface دیگر اضافه کنیم:
1 2 3 4 5 |
export interface OwnerForUpdate{ name: string; dateOfBirth: string; address: string; } |
و همچنین تابع updateOwner را در فایل owner-repository.service تغییر دهید:
1 |
public updateOwner = (route: string, owner: OwnerForUpdate) => |
در نهایت، ما به فایل component خود باز می گردیم و عمل update را اجرا می کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public updateOwner = (ownerFormValue) => { if (this.ownerForm.valid) this.executeOwnerUpdate(ownerFormValue); } private executeOwnerUpdate = (ownerFormValue) => { const ownerForUpd: OwnerForUpdate = { name: ownerFormValue.name, dateOfBirth: this.datePipe.transform(ownerFormValue.dateOfBirth, 'yyyy-MM-dd'), address: ownerFormValue.address } const apiUri: string = `api/owner/${this.owner.id}`; this.repository.updateOwner(apiUri, ownerForUpd) .subscribe({ next: (_) => { const config: ModalOptions = { initialState: { modalHeaderText: 'Success Message', modalBodyText: 'Owner updated successfully', okButtonText: 'OK' } }; this.bsModalRef = this.modal.show(SuccessModalComponent, config); this.bsModalRef.content.redirectOnOk.subscribe(_ => this.redirectToOwnerList()); }, error: (err: HttpErrorResponse) => this.errorHandler.handleError(err) }) } |
این تقریباً همان logic تابع createOwner است. فقط اینکه ما اینجا هیچ پارامتری برای ویژگی :next نداریم: زیرا API ما یک object را به عنوان response به درخواست PUT بر نمی گرداند.
البته ما اینجا به تابع redirectToOwnerList نیز نیاز داریم:
1 2 3 |
public redirectToOwnerList = () => { this.router.navigate(['/owner/list']); } |
حال می توانید آن را امتحان کرده و تعدادی update انجام دهید. سعی کنید response های از نوع success و error را در سرور ایجاد کرده تا component های modal را نیز تست کنید. سپس، می توانید بررسی کنید که آیا اعتبار سنجی فرم کار می کند یا خیر.
نتیجه گیری
در این مقاله، نحوه ایجاد template (HTML) به عنوان قسمتی از اقدام update، و همچنین اقداماتی را که باید در فایل component (.ts) اجرا کنیم را یاد گرفتیم.
در قسمت بعدی این آموزش گام به گام، قسمت delete پروژه را می نویسیم و کم کم قسمت کد نویسی این آموزش را با هم می بندیم.