هنگام ارسال درخواست های HTTP به سرور خود، باید از Angular HttpClient استفاده کنیم. البته، درست است که میتوانیم تمام درخواستهای HTTP از هر component را handle کرده و response را نیز پردازش کنیم، اما این شیوه خوبی نیست. خیلی بهتر است که یک repository برای درخواست های خود ساخته و سپس URI درخواست را به آن repository ارسال کنید. در ادامه، این repository است که باید به بقیه چیزها رسیدگی کند.
اگر می خواهید تمام آموزشهای لازم و پایه مربوط به آموزش گام به گام Angular را ببینید ، لطفاً روی این لینک کلیک کنید: مقدمه آموزش گام به گام Angular.
بنابراین، ابتدا با فایل های environment شروع می کنیم.
کار با فایلهای Environment
در حالی که این پروژه در حالت توسعه است، آدرس endpoint سرور، http://localhost:[Port Number] است. endpoint اپلیکیشن در محیط production متفاوت است، چیزی شبیه به www.accountowner.com. بنابراین، ما می خواهیم که Angular این را در نظر بگیرد. با این اوصاف،Angular در development mode، باید درخواستها را به development URI و در محیط production به آدرس دیگری ارسال کند.
پس آن را پیاده سازی میکنیم.
در پنجره Code explorer ویژوال استودیو، ما میخواهیم پوشه environments را جستجو کنیم: src/environments. داخل این پوشه، میتوانیم دو فایل environment.prod.ts
, و environment.ts
را پیدا کنیم. اولی را برای تنظیم production و دومی را برای تنظیم حالت development استفاده خواهیم کرد.
بنابراین، کار خود را با اصلاح environment.prod.ts شروع میکنیم:
1 2 3 4 |
export const environment = { production: true, urlAddress: 'http://www.accountowner.com' }; |
سپس فایل environment.ts را تغییر میدهیم:
1 2 3 4 |
export const environment = { production: false, urlAddress: 'http://localhost:5000' }; |
اکنون می خواهیم یک service ایجاد کنیم که می توانیم از آن برای دریافت environment urlAddress
معتبر استفاده کنیم.
یک فایل environment-url.service.ts جدید ایجاد میکنیم:
1 |
ng g service shared/services/environment-url --skip-tests |
این دستور یک پوشه جدید shared/service ایجاد می کند و در داخل این پوشه نیز، یک فایل service به این صورت ایجاد میکنیم:
1 |
CREATE src/app/shared/services/environment-url.service.ts (143 bytes) |
در مورد Service های Angular
Service ها فقط کلاس هایی هستند که business logic مرتبط با component های ما را در اختیار ما قرار می دهند. این Service ها باید با استفاده از تزریق سازنده (constructor injection) به یک component تزریق شوند. علاوه بر این، زمانی که logic را از یک component به Service استخراج کنیم، کد ما maintainable و readable می شود.
وقتی می خواهیم از یک Service استفاده کنیم، باید آن را به سازنده یک component تزریق کنیم. بنابراین آن همیشه با @Injectable
decorator مزین می شود.
ما باید هر زمان که کدی داریم که میتوانیم دوباره از آن در component های دیگر استفاده کنیم یا بخشی از کد را از component های خود استخراج کنیم، باید از Service ها استفاده کنیم.
با این اوصاف، اجازه دهید کار خود را با فایل های environment با اصلاح فایل environment-url.service.ts ادامه دهیم:
1 2 3 4 5 6 7 8 9 10 11 |
import { environment } from './../../../environments/environment'; import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class EnvironmentUrlService { urlAddress: string = environment.urlAddress; constructor() { } } |
ویژگی urlAddress، مقدار urlAddress تعریف شده در فایل environment را دریافت میکند. Angular می داند که آیا این یک محیط production است یا یک محیط development و قرار است مقدار معتبری از آن urlAddress را در اختیار ما قرار دهد. در فایل angular.json میتوانیم این را بررسی کنیم:
1 2 3 4 5 6 7 8 9 |
"configurations": { "production": { ... "fileReplacements": [ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.prod.ts" } ], |
اینجا میبینیم که Angular، فایلها را در حالت production جایگزین میکند.
ایجاد فایل Angular Repository
اکنون میتوانیم HttpClientModule را تنظیم کرده و repository service را ایجاد کنیم.
ابتدا باید HttpClientModule را داخل فایل import ،app.module.ts کنیم:
1 |
import { HttpClientModule } from '@angular/common/http'; |
سپس آن را در داخل آرایه imports
قرار می دهیم:
1 2 3 4 5 6 7 |
imports: [ BrowserModule, AppRoutingModule, BrowserAnimationsModule, HttpClientModule, CollapseModule.forRoot() ] |
پس از آن، یک فایل service ایجاد میکنیم و نام آن را owner-repository.service.ts میگذاریم. ما آن را در همان پوشه ای که environment service در آن قرار دارد قرار می دهیم. در این service، ما میخواهیم درخواستهای GET، POST، PUT و DELETE را برای موجودیت owner از API سرور خود ایجاد کنیم:
1 |
ng g service shared/services/owner-repository --skip-tests |
پس از ایجاد فایل، ما یک پوشه interfaces_ جدید در پوشه app ایجاد می کنیم و یک فایل owner.model.ts جدید به آن اضافه می کنیم:
1 2 3 4 5 6 |
export interface Owner{ id: string; name: string; dateOfBirth: Date; address: string; } |
هنگامی که interface خود را در جای خود قرار دادیم، می توانیم فایل repository را به این صورت اصلاح کنیم:
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 38 |
import { Owner } from './../../_interfaces/owner.model'; import { EnvironmentUrlService } from './environment-url.service'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class OwnerRepositoryService { constructor(private http: HttpClient, private envUrl: EnvironmentUrlService) { } public getOwners = (route: string) => { return this.http.get<Owner[]>(this.createCompleteRoute(route, this.envUrl.urlAddress)); } public createOwner = (route: string, owner: Owner) => { return this.http.post<Owner>(this.createCompleteRoute(route, this.envUrl.urlAddress), owner, this.generateHeaders()); } public updateOwner = (route: string, owner: Owner) => { return this.http.put(this.createCompleteRoute(route, this.envUrl.urlAddress), owner, this.generateHeaders()); } public deleteOwner = (route: string) => { return this.http.delete(this.createCompleteRoute(route, this.envUrl.urlAddress)); } private createCompleteRoute = (route: string, envAddress: string) => { return `${envAddress}/${route}`; } private generateHeaders = () => { return { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) } } } |
توضیح کد Repository
این کد را با یکدیگر بررسی میکنیم.
ابتدا Angular HttpClient و متغیر environment را به سازنده تزریق می کنیم. سپس function هایی را ایجاد می کنیم که request های ما را پوشش میدهند. تابع getOwners یک wrapper برای درخواست GET است. این تابع، پارامتر route از نوع رشته (api/owner) را گرفته و سپس آن را با متغیر environment (localhost یا www…) ترکیب می کند. پس از همه اینها، اگر محیط، محیط development باشد، مسیری مانند http://localhost:5000/api/owner را خواهیم داشت که کاملاً با نیازهای ما در سمت سرور مطابقت دارد.
تابع دوم، createOwner است که یک wrapper برای درخواست POST است. این تابع نیز یک مسیر را تولید می کند، اما علاوه بر این، یک body (entity که ما در حال ایجاد آن هستیم) را دریافت کرده و header ها را تولید می کند. برای این مثال، ما فقط در حال ایجاد Content-Type در داخل header هستیم. اما اگر به مقادیر اضافی در header نیاز داشته باشیم، میتوانیم یک جفت کلید-مقدار دیگر را نیز در آبجکت HttpHeaders اضافه کنیم.
تابع updateOwner تقریباً مشابه تابع create است، با این تفاوت که درخواست PUT را ارسال می کند. در نهایت، تابع deleteOwner یک wrapper برای درخواست DELETE است که مسیری مانند (api/owner/id) را می پذیرد. هر دوی این توابع (put and delete) فاقد یک strongly typed HTTP method هستند، زیرا ما انتظار هیچ آبجکتی را به عنوان response از سرور نداریم. اگر همه چیز خوب پیش برود، ما یک 204 را به عنوان response بدون response body دریافت می کنیم.
Subscription در Call های HTTP
این توابع wrapper برای کار کردن نیاز به subscription دارند. در این پست، ما فقط در حال ایجاد یک repository با call های HTTP هستیم. اما به محض اینکه شروع به ایجاد صفحات خود کنیم، قصد داریم از subscription استفاده کنیم.
در حال حاضر، ما فقط یک نمونه از subscription را به شما نشان می دهیم:
1 2 3 4 5 6 7 8 9 10 |
owners: Owner[]; constructor(private repo: OwnerRepositoryService) { } private consumeGetFromRepository = () => { this.repo.getOwners('api/owner') .subscribe(own => { this.owners = own; }) } |
همانطور که اینجا متوجه شده اید، ما تابع getOwners را از repository فراخوانی می کنیم، اما تا زمانی که تابع subscribe را فراخوانی نکنیم، این تابع اجرا نخواهد شد. نتیجه response قرار است در پارامتر own
ذخیره شود.
نتیجه گیری
بسیار عالی، اکنون ما repository خود را آماده کرده ایم و آماده ایجاد component هایی هستیم که از توابع این repository برای نمایش نتایج در مرورگر استفاده می کنند.
در قسمت بعدی این آموزش گام به گام، قصد داریم نحوه استفاده از بارگذاری محتوای lazy در Angular را به شما نشان دهیم. علاوه بر این، result data را در صفحه نشان خواهیم داد.