Di chuyển các dịch vụ AngularJS của bạn sang Angular với ngUpgrade
Trong hướng dẫn cuối cùng của ta , ta đã đề cập đến cách cài đặt mọi thứ ta cần để bắt đầu nâng cấp từ AngularJS lên Angular. Ta cũng đã đề cập đến cách viết lại và hạ cấp các thành phần.
Trong hướng dẫn này, bạn sẽ làm việc với các dịch vụ trong một dự án ngUpgrade. Cụ thể, bạn sẽ:
Viết lại một dịch vụ AngularJS thành Angular
Chuyển một lời hứa có thể quan sát được
Hạ cấp dịch vụ để nó vẫn hoạt động với mã AngularJS của ta
Chuyển một lời hứa thành một lời hứa có thể quan sát được
Điểm xuất phát của ta
Hãy dành một phút để sao chép hoặc phân nhánh dự án mẫu này trên GitHub (đừng quên chạy npm install
trong cả folder public
và server
). Kiểm tra commit này để xem điểm khởi đầu của ta :
-
- git checkout 083ee533d44c05db003413186fbef41f76466976
-
Ta có một dự án Hệ thống đặt hàng mà ta có thể sử dụng để làm việc thông qua ngUpgrade. Nó sử dụng kiến trúc thành phần, TypeScript và Webpack (với các bản dựng cho cả phát triển và production ). Ta cũng đã cài đặt và khởi động Angular và ngUpgrade, và thành phần home đã được viết lại thành Angular.
(Nếu bạn bị mất bất kỳ điều nào trong số đó, ta sẽ đề cập đến tất cả trong khóa học video toàn diện Nâng cấp AngularJS .)
Một lưu ý nhanh: Mọi thứ thay đổi nhanh chóng trong Angular và RxJS. Nếu bạn đang sử dụng Angular 4.3+ hoặc 5+, bạn sẽ thấy một vài khác biệt nhỏ ở đây so với dự án mẫu. Dự án mẫu sử dụng Http
trong các dịch vụ cho các cuộc gọi HTTP như GET
và POST
. Ta sẽ sử dụng HttpClient
mới đã được thêm vào version 4.3+, có chức năng cần thiết cho các mục đích của hướng dẫn này .. RxJS cũng đã thực hiện một số thay đổi kể từ version 5.5 trong cách nhập mọi thứ, vì vậy ta ' Tôi sẽ sử dụng phong cách mới đó ở đây.
Viết lại một Dịch vụ AngularJS
Khi thực hiện một ngUpgrade, thật thông minh khi chọn từng tuyến một và làm việc từ dưới lên. Bạn có thể tận dụng lợi thế của việc giữ Angular và AngularJS chạy cạnh nhau mà không lo bị hỏng ứng dụng.
Vì ta đã thực hiện tuyến đường về nhà trong hướng dẫn trước, ta hiện đã sẵn sàng để bắt đầu tuyến đường dành cho customers
. Ta sẽ bắt đầu bằng cách viết lại CustomerService và hạ cấp nó để cung cấp cho các thành phần AngularJS của ta . Sau đó, ta sẽ xem xét việc sử dụng cả những điều có thể quan sát được và những lời hứa trong dịch vụ, để bạn có thể chọn cho mình cái nào phù hợp nhất với bạn trong quá trình di chuyển của bạn .
Thêm HttpClient vào NgModule
Trước khi viết lại CustomerService, ta phải nhập rõ ràng HttpClientModule của Angular vào NgModule của ta cho ứng dụng ( app.module.ts ) để thực hiện các cuộc gọi HTTP. Điều này khác với trong Angular JS, nơi mọi thứ được bao gồm theo mặc định. Trong Angular, ta cần phải rõ ràng về những phần nào của Angular mà ta muốn sử dụng. Mặc dù thoạt đầu nó có vẻ không hiệu quả, nhưng điều này rất tốt vì nó giúp giảm dấu vết của ứng dụng của ta bằng cách không tự động nhập mã không sử dụng.
Vì vậy, sau dòng 3, ta sẽ nhập nó như thế này:
import { HttpClientModule } from '@angular/common/http';
Sau đó, ta cần thêm module đó vào mảng imports
sau UpgradeModule trên dòng 12:
//app.module.ts
@NgModule({
imports: [
BrowserModule,
UpgradeModule,
HttpClientModule
],
declarations: [
HomeComponent
],
entryComponents: [
HomeComponent
]
})
Bây giờ ta có thể sử dụng HttpClientModule trong suốt ứng dụng của bạn . Ta chỉ cần nhập nó một lần và ta có thể sử dụng nó cho tất cả các dịch vụ còn lại của bạn trong suốt ứng dụng.
Viết lại Dịch vụ Khách hàng
Bây giờ ta đã thêm HttpClientModule vào module ứng dụng Angular của ta , ta đã sẵn sàng để viết lại CustomerService trong Angular. Sau đó, ta sẽ hạ cấp nó xuống để ta vẫn có thể sử dụng nó trong các thành phần Angular JS cũng như các thành phần Angular của ta .
Điều đầu tiên ta sẽ làm là đổi tên customerService.ts
file để customer.service.ts
để nó theo quy ước đặt tên hiện tại.
Bây giờ, ta hãy mở file . Bạn sẽ thấy rằng ta đang sử dụng một lớp ES2015:
//customer.service.ts
class CustomerService{
$http: any;
constructor($http) {
this.$http = $http;
}
getCustomers(){
return this.$http.get('/api/customers')
.then((response) => response.data);
}
getCustomer(id){
return this.$http.get(`/api/customers/${id}`)
.then((response) => response.data);
}
postCustomer(customer){
return this.$http.post('/api/customers', customer)
.then((data) => data);
}
}
CustomerService.$inject = ['$http'];
export default CustomerService;
Các dịch vụ Angular 2+ là các lớp mà ta xuất, nhưng ta thêm chú thích Injectable()
. Đã qua rồi cái thời cố gắng ghi nhớ các nhà máy, dịch vụ, nhà cung cấp và cách tạo ra từng nhà máy. Trong Angular, một dịch vụ là một dịch vụ và nó chỉ là một lớp được xuất với chú thích có thể tiêm vào. Đó không phải là một sự cứu trợ lớn sao?
Chuẩn bị mã
Điều đầu tiên ta có thể làm là xóa hai dòng cuối cùng trong file này. Ta không cần mảng AngularJS $inject
injection nữa, và thay vì sử dụng export default
, ta sẽ thêm từ khóa export
trước khai báo lớp:
export CustomerService { //etc.
Bây giờ tôi đã sẵn sàng nhập hai thứ từ Angular lên ở đầu file . Đầu tiên là chú thích Injectable()
đã được đề cập trước đây:
import { Injectable } from '@angular/core';
Tiếp theo, ta cần HttpClient:
import { HttpClient } from '@angular/common/http';
Bây giờ ta đã sẵn sàng để biến đây thành một dịch vụ Angular.
Cập nhật Lớp dịch vụ lên Angular
Đầu tiên, hãy thêm chú thích Injectable()
vào CustomerService của ta , ngay phía trên lớp:
@Injectable()
Không có đối tượng tùy chọn nào được chuyển vào chú thích này.
Điều tiếp theo ta cần làm là thay thế tất cả các tham chiếu đến dịch vụ $http
của AngularJS bằng HttpClient của Angular. Thay vào đó, ta sẽ sử dụng viết tắt http
cho việc này, vì vậy hãy thực hiện tìm và thay thế trong tài liệu này, thay đổi $http
thành http
, với điều kiện hầu hết các lệnh gọi sẽ giống nhau:
Bây giờ ta cần thay đổi một điều về cách tạo thuộc tính http của ta . Thay vì điều này:
//customer.service.ts
class CustomerService{
http: any;
constructor(http) {
this.http = http;
}
… Ta sẽ xóa dòng sáu khai báo thuộc tính công cộng của loại http
any
. Thay vào đó, trong hàm tạo của ta , hãy thêm từ khóa private
trước http
và chỉ định rằng từ khóa đó thuộc loại HttpClient
:
//customer.service.ts
export class CustomerService{
constructor(private http: HttpClient) { }
Với việc chèn phụ thuộc của Angular, ta đang khởi tạo một version riêng của dịch vụ HttpClient trên CustomerService của ta . Bạn cũng có thể thấy rằng, với từ khóa private
, ta không cần đặt version lớp của http
bằng version được đưa vào của ta (nó làm điều này đằng sau mức thấp cho ta ).
Những gì ta có bây giờ là phần xương trần của một dịch vụ Angular, nhưng bây giờ bạn sẽ thấy những dòng chữ nguệch ngoạc màu đỏ đó bên dưới mọi nơi ta sử dụng .then
. Bạn có thể thấy rằng IntelliSense đang cho ta biết rằng thuộc tính sau đó không tồn tại trên loại phản hồi có thể quan sát được:
Chuyện gì đang xảy ra ở đó? Hãy giải quyết vấn đề đó tiếp theo.
Chuyển đổi Khả năng quan sát thành Hứa hẹn
Ta đã có phần lớn dịch vụ khách hàng được viết lại thành dịch vụ Angular, nhưng ta gặp một chút vấn đề khi cố gắng sử dụng .then
trên các cuộc gọi http này. Đó là bởi vì HttpClient trong Angular trả về một giá trị có thể quan sát được thay vì một lời hứa. Ta có hai lựa chọn ở đây:
Cách thực tế: chuyển đổi những phản hồi này thành lời hứa và phần còn lại của ứng dụng của ta sẽ hoạt động như vậy, hoặc
Cách thú vị: giữ những phản hồi này ở dạng có thể quan sát được và cập nhật các thành phần của ta .
Với bất kỳ trình tái cấu trúc hoặc nâng cấp quy mô lớn nào, mục tiêu luôn là để mất ít thời gian cập nhật nhất có thể trong ứng dụng của bạn. Cách tiếp cận được đề xuất trước tiên là chuyển đổi lời gọi thành lời hứa. Bằng cách đó, bạn có thể xác định các thành phần và các phần khác của ứng dụng phụ thuộc vào dịch vụ và các lệnh gọi của nó. Sau khi thực hiện xong, bạn có thể chuyển đổi lần lượt các lệnh gọi thành có thể quan sát và cập nhật từng thành phần cho phù hợp. Vì vậy, trước tiên, hãy chuyển một dịch vụ tới Angular và làm cho nó hoạt động. Sau đó, hãy lo lắng về việc sử dụng các thiết bị quan sát khi bạn cảm thấy thời điểm thích hợp.
Vì vậy, trước tiên hãy chuyển đổi các cuộc gọi thành các lời hứa. Tuy nhiên, đừng lo lắng - ta sẽ thực hiện điều thú vị và chuyển đổi một cuộc gọi thành một cuộc gọi có thể quan sát được.
Sử dụng ToPromise Operator
Để chuyển đổi các file quan sát thành các hứa hẹn, trước tiên ta cần nhập từ RxJS, thư viện xử lý các file có thể quan sát. Sau khi nhập Angular, ta chỉ cần thêm:
import { Observable } from 'rxjs/Observable';
Điều này cho phép ta sử dụng các chức năng khác nhau cho đối tượng quan sát được cung cấp bởi RxJS.
Phương thức toPromise
cho phép ta chuyển đổi những thứ có thể quan sát thành những lời hứa. Nó từng là một lần nhập riêng trong các version trước của RxJS, nhưng giờ đây đã được đưa vào Observable
. Nhập các toán tử riêng lẻ là một mẫu phổ biến trong RxJS, nhưng việc tìm ra toán tử nào bạn cần và nơi chúng cư trú trong thư viện có thể hơi khó khăn. Đảm bảo xem qua các nguồn tài liệu mà RxJS cung cấp , cũng như tài liệu Angular về RxJS .
Bây giờ ta có thể sử dụng toán tử toPromise
trước mỗi toPromise
.then
trong các cuộc gọi của bạn . Khi bạn làm điều đó, bạn cũng sẽ thấy lỗi cho biết rằng .data
không phải là một thuộc tính tồn tại trên loại "đối tượng". Đó là bởi vì phản hồi đã trả về đối tượng dữ liệu bên trong phản hồi HTTP. Tất cả những gì ta cần làm sau đó là xóa .data
. Điều này khác với những ngày của dịch vụ Http
ban đầu, nơi ta cần gọi một hàm .json
để trả về dữ liệu.
Một điều nữa. Vì ta có những lợi ích của TypeScript, hãy thêm kiểu trả về vào mỗi hàm này. Trong TypeScript, tốt nhất là chỉ định các loại khi có thể, mặc dù về mặt kỹ thuật, nó không bắt buộc. Vì vậy, sau mỗi tên hàm, ta sẽ thêm :Promise<any>
.
Các chức năng đã hoàn thành trong dịch vụ sẽ giống như sau:
//customer.service.ts
getCustomers():Promise<any> {
return this.http.get('/api/customers')
.toPromise()
.then(response => response);
}
getCustomer(id):Promise<any> {
return this.http.get(`/api/customers/${id}`)
.toPromise()
.then(response => response);
}
postCustomer(customer):Promise<any> {
return this.http.post('/api/customers', customer)
.toPromise()
.then(data => data);
}
Như vậy, ta đã chuyển đổi thành công những thứ có thể quan sát được trong lời gọi của ta thành những lời hứa.
Hạ cấp dịch vụ khách hàng
Bây giờ ta đã chuyển đổi khả năng quan sát của bạn thành lời hứa, ta đã sẵn sàng hạ cấp dịch vụ khách hàng để các thành phần AngularJS chưa được di chuyển vẫn có thể sử dụng nó.
Quá trình này tương tự như khi ta hạ cấp thành phần home trong hướng dẫn trước. Điều đầu tiên ta cần làm là nhập hàm downgradeInjectable
từ thư viện ngUpgrade, giống như ta đã nhập downgradeComponent
cho thành phần home. Vì vậy, sau dòng hai, ta sẽ thêm:
import { downgradeInjectable } from '@angular/upgrade/static';
Ta cũng cần khai báo một biến có tên là angular
giống như ta đã làm trong home component. Vì vậy, sau dòng thứ tư, ta sẽ thêm:
declare var angular: angular.IAngularStatic;
Sau đó, ở cuối file của ta , ta sẽ đăng ký dịch vụ của ta như một nhà máy bị hạ cấp. Vì vậy, sau khi kết thúc lớp học, ta sẽ nhập:
angular.module('app')
.factory('customerService', downgradeInjectable(CustomerService));
Ta đã hạ cấp CustomerService để có sẵn cho AngularJS. Đây là dịch vụ đã hoàn thành:
//customer.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { downgradeInjectable } from '@angular/upgrade/static';
declare var angular: angular.IAngularStatic;
@Injectable()
export class CustomerService {
constructor(private http: HttpClient) {}
getCustomers():Promise<any> {
return this.http.get('/api/customers')
.toPromise()
.then(response => response);
}
getCustomer(id):Promise<any> {
return this.http.get(`/api/customers/${id}`)
.toPromise()
.then(response => response);
}
postCustomer(customer):Promise<any> {
return this.http.post('/api/customers', customer)
.toPromise()
.then((data) => data);
}
}
angular.module('app')
.factory('customerService', downgradeInjectable(CustomerService));
Di chuyển Dịch vụ sang Mô-đun Angular
Dịch vụ khách hàng của ta đã được viết lại thành dịch vụ Angular và được hạ cấp để có sẵn cho AngularJS. Bây giờ ta cần xóa tham chiếu của bạn trong module AngularJS và thêm nó vào module Angular của ta .
Xóa nội dung khỏi module AngularJS
Đầu tiên, hãy mở module AngularJS của ta ( app.module.ajs.ts
). Bạn có thể bỏ dòng 22:
import CustomerService from './customers/customerService';
… Cũng như dòng 41:
.service('customerService', CustomerService)
Đó là tất cả những thay đổi bạn cần thực hiện trong module này.
Di chuyển Dịch vụ sang Mô-đun Angular
Bây giờ, hãy thêm dịch vụ của ta vào NgModule
của ta trong app.module.ts
để mã Angular của ta có thể truy cập nó. Điều đầu tiên ta cần làm là nhập dịch vụ sau dòng thứ bảy:
import { CustomerService } from './customers/customer.service';
Bây giờ để đăng ký dịch vụ khách hàng của ta trong ứng dụng của ta , ta cần thêm một loạt các providers
vào NgModule
sau mảng entryComponents
và thêm CustomerService của ta vào đó:
//app.module.ts
providers: [
CustomerService
]
Mảng providers
là nơi ta đăng ký tất cả các dịch vụ của bạn trong ứng dụng. Và bây giờ ta đã đăng ký dịch vụ khách hàng trong NgModule của bạn và sẵn sàng hoạt động.
Ghi chú nhanh về biên dịch AOT
Phương pháp hạ cấp này - đăng ký dịch vụ đã hạ cấp trong file dịch vụ và xóa nó khỏi file module AngularJS - hoạt động hoàn toàn tốt để phát triển hoặc nếu bạn định viết lại nhanh ứng dụng của bạn trước khi triển khai. Tuy nhiên, trình biên dịch Angular AOT để production sẽ không hoạt động với phương pháp này. Thay vào đó, nó muốn tất cả các đăng ký đã hạ cấp của ta trong module AngularJS.
Việc hạ cấp giống hệt nhau, nhưng thay vào đó bạn:
Nhập
downgradeInjectable
trongapp.module.ajs.ts
(bạn đã cóangular
trong đó nên không cần khai báo).Thay đổi cách nhập
CustomerService
đểimport { CustomerService } from './customers/customer.service';
kể từ khi ta chuyển sang xuất được đặt tên.Thay đổi đăng ký dịch vụ thành đăng ký nhà máy chính xác như hiển thị ở trên.
Kiểm tra chức năng của ứng dụng
Tốt hơn hết ta nên đảm bảo ứng dụng của ta vẫn đang chạy. Hãy bắt đầu API Express của ta , sau đó chạy server phát triển Webpack của ta . Mở một terminal và chạy các lệnh sau để khởi động Express:
cd server
npm start
Sau đó, mở một terminal khác và chạy các lệnh sau để khởi động Webpack:
cd public
npm run dev
Bạn sẽ thấy mọi thứ được biên dịch và đóng gói một cách chính xác.
Bây giờ, mở trình duyệt và truy cập localhost:9000
. Hãy chuyển đến tuyến đường của khách hàng và xem liệu dịch vụ có hoạt động không:
Ta có thể kiểm tra lại xem mình có đang sử dụng dịch vụ Angular được viết lại hay không bằng cách chuyển đến tab nguồn trong công cụ dành cho nhà phát triển Chrome, chuyển xuống folder khách hàng và nhấp vào nguồn CustomerService:
Điều này cho thấy dịch vụ được viết lại của ta . Ta đã cập nhật dịch vụ lên Angular, nhưng nó đang được sử dụng trong cả thành phần khách hàng và thành phần bảng khách hàng, cả hai dịch vụ này vẫn nằm trong AngularJS.
Sử dụng GetCustomers như một người có thể quan sát được
Bây giờ ta đã hạ cấp CustomerService và đang hoạt động, hãy vui vẻ và sử dụng lệnh gọi getCustomers
đó như một cách có thể quan sát được. Bằng cách đó, ta có thể bắt đầu tận dụng tất cả các tính năng mới của vật thể quan sát. Điều này sẽ hơi phức tạp một chút, bởi vì ta đang sử dụng lệnh gọi trong cả thành phần khách hàng và thành phần đơn đặt hàng, cả hai đều chưa được viết lại thành Angular. Đừng lo lắng - tôi sẽ chỉ cho bạn cách thực hiện việc này từng bước.
Quay lại mã dịch vụ khách hàng, điều đầu tiên ta cần làm là thay đổi kiểu trả về trên dòng 16 thành Observable<any>
. Tất nhiên bây giờ, TypeScript đang phàn nàn với ta vì ta đang chuyển đổi sangPromise, vì vậy ta chỉ cần xóa cả hai hàm toPromise
và then
. Nó trông giống như sau:
getCustomers():Observable<any> {
return this.http.get('/api/customers');
}
Bây giờ ta cần cập nhật thành phần khách hàng của bạn để sử dụng một thành phần có thể quan sát được thay vì một lời hứa. Ta sẽ làm điều đó tiếp theo.
Sử dụng một cấu phần khách hàng có thể quan sát được
Cuộc gọi getCustomers
của ta hiện đang trở lại khi có thể quan sát được. Hãy cập nhật thành phần khách hàng của ta ( customers.ts
) để sử dụng một lời hứa có thể quan sát được thay vì một lời hứa. Thành phần khách hàng vẫn là một thành phần AngularJS và điều đó ổn, ta chưa cần phải làm gì với nó, nhưng hãy sử dụng một chút TypeScript để giúp ta . Hãy nhập CustomerService của ta ở đầu file của ta :
import { CustomerService } from './customer.service';
Bây giờ ta đã nhập CustomerService, ta có thể chỉ định loại CustomerService được đưa vào của ta trong định nghĩa hàm bộ điều khiển của ta :
//customers.ts
function customersComponentController(customerService: CustomerService){
Bây giờ ta có lợi thế về việc TypeScript phàn nàn về .then
giống như nó đã làm trong CustomerService của ta . Nó biết rằng lời gọi getCustomers
được cho là trả về một file có thể quan sát được và .then
không tồn tại trên một file có thể quan sát được.
Cách ta sử dụng một thành phần có thể quan sát được trong một thành phần, cho dù đó là một thành phần AngularJS hay Angular, là đăng ký vào nó. Trước khi có thể đăng ký có thể quan sát này, ta cần nhập Có thể Observable
giống như ta đã làm trong dịch vụ. Vì vậy, bên trên nhập CustomerService của ta , ta sẽ thêm:
import { Observable } from 'rxjs/observable';
Điều này sẽ cho phép ta sử dụng các chức năng trên có thể quan sát, bao gồm cả subscribe
. Vì vậy, bây giờ trên dòng 18 bên trong hàm $onInit
ta , ta chỉ có thể thay đổi then
để subscribe
và mọi thứ khác có thể giữ nguyên.
Hãy đi xem xét trình duyệt và xem liệu điều này có hoạt động như mong đợi hay không. Nếu bạn xem xét tuyến khách hàng, bạn sẽ thấy rằng mọi thứ đang hoạt động như nhau. Tuy nhiên, nếu ta chuyển sang tab Đơn đặt hàng, ta sẽ thấy một vấn đề lớn: không có dữ liệu và TypeError: Cannot read property 'fullName' of undefined
trong console . Những gì đang xảy ra ở đây?
Hóa ra thành phần đơn đặt hàng cũng sử dụng lệnh gọi getCustomers
, nhưng nó vẫn đang cố gắng sử dụng nó như một lời hứa. Hãy khắc phục điều đó.
Sửa thành phần đơn hàng
Khi ta viết lại lời gọi getCustomers của bạn để trở thành một điều có thể quan sát được thay vì một lời hứa, ta đã vô tình phá vỡ thành phần orders/orders.ts
( orders/orders.ts
), thành phần này vẫn còn trong AngularJS. Đó là bởi vì trong hàm $onInit
ta , ta đang sử dụng $q.all
để đợi hai lời hứa trả về trước khi ta gán bất kỳ dữ liệu nào vào mô hình xem của bạn :
vm.$onInit = function() {
let promises = [orderService.getOrders(), customerService.getCustomers()];
return $q.all(promises).then((data) => {
vm.orders = data[0];
vm.customers = data[1];
vm.orders.forEach(function (order) {
var customer = _.find(vm.customers, function (customer) {
return order.customerId === customer.id;
});
order.customerName = customer.fullName;
});
});
};
Đây là một mô hình phổ biến trong AngularJS.
Một giải pháp cho vấn đề này là chỉ viết lại thành phần đơn đặt hàng thành Angular và cũng viết lại dịch vụ đặt hàng. Nhưng, trong thế giới thực, điều đó không phải lúc nào cũng khả thi ngay lập tức. Lưu ý , trong bất kỳ quá trình tái cấu trúc quy mô lớn nào, ưu tiên đầu tiên là đảm bảo ta giảm thiểu thời gian chết và có thể có một ứng dụng có thể phân phối liên tục mà ta luôn có thể triển khai cho quá trình production .
Tuy nhiên, điều gì sẽ xảy ra nếu thành phần đơn đặt hàng phức tạp hơn nhiều và ta không có thời gian để viết lại nó? Trong trường hợp đó, ta có hai lựa chọn: ta có thể chuyển đổi lệnh gọi getCustomers
thành một lời hứa trong thành phần đơn hàng hoặc ta có thể chuyển đổi lời hứa getOrders
thành một lời hứa có thể quan sát được.
Để chuyển đổi getCustomers
thành một lời hứa trong thành phần, ta chỉ thực hiện chính xác như những gì ta đã làm trước đó trong dịch vụ - nhập Observable
từ RxJS và thêm toán tử toPromise
sau getCustomers
. Thật dễ dàng và đó là một thủ thuật hữu ích nếu bạn không thể có thời gian để cấu trúc lại thành phần này để sử dụng các thiết bị có thể quan sát được. Tuy nhiên, điều đó không hoàn toàn mong muốn, vì mục tiêu tầm xa của ta là loại bỏ hoàn toàn những lời hứa và chuyển hoàn toàn sang có thể quan sát được. Vì vậy, tôi nên chuyển lời gọi getOrders
của ta thành một lệnh có thể quan sát được ở đây.
Chuyển đổi getCustomers
to a Promise
Hãy chuyển đổi getOrders
thành có thể quan sát được. Điều đầu tiên ta sẽ làm là nhập CustomerService của ta ở đầu file giống như ta đã làm trong thành phần khách hàng:
import { CustomerService } from '../customers/customer.service';
Sau đó, ta có thể chỉ định loại CustomerService được đưa vào của ta trong định nghĩa hàm bộ điều khiển của ta :
//orders.ts
function ordersComponentController(orderService, customerService: CustomerService, $q) {
Để chuyển đổi lời gọi getOrders
thành có thể quan sát được, ta sẽ sử dụng hai phương thức tĩnh trên có thể quan sát được gọi là fromPromise
và forkJoin
. Phương thức fromPromise
cho phép ta chuyển đổi một lời hứa thành một phương thức có thể quan sát được và forkJoin
cho phép ta đăng ký nhiều phương thức có thể quan sát được. Vì vậy, bây giờ bạn có thể đoán rằng điều đầu tiên ta cần làm là nhập hai phương thức đó ở đầu file của ta :
import { fromPromise } from 'rxjs/observable/fromPromise';
import { forkJoin } from 'rxjs/observable/forkJoin';.
Bây giờ ta có thể thực hiện một số công việc trong hàm $onInit
. Trên dòng 21, hãy khai báo một biến có tên là OrderData và sử dụng phương thức fromPromise:
let ordersData = fromPromise(orderService.getOrders());
Bây giờ hãy viết lại $q.all
để sử dụng forkJoin
thay thế. Vì vậy, trước tiên, ta sẽ thay thế return $q.all
bằng forkJoin
. Ta cần phải truyền vào một mảng, vì vậy hãy di chuyển mảng promises
và thêm ordersData
vào phía trước của nó và sau đó chỉ cần loại bỏ khai báo promises
. Cuối cùng, hãy thay đổi .then
thành .subscribe
giống như với một .subscribe
có thể quan sát được. Đây là hàm $onInit
đã hoàn thành của ta :
vm.$onInit = function() {
let ordersData = fromPromise(orderService.getOrders());
forkJoin([ordersData, customerService.getCustomers()]).subscribe((data) => {
vm.orders = data[0];
vm.customers = data[1];
vm.orders.forEach(function (order) {
var customer = _.find(vm.customers, function (customer) {
return order.customerId === customer.id;
});
order.customerName = customer.fullName;
});
});
};
Hãy tóm tắt lại những gì ta đã làm ở đây. Đầu tiên, ta đã gọi fromPromise
và chuyển đổi getOrders
gọi getOrders
của ta từ một lời hứa thành một lời hứa có thể quan sát được. Sau đó, ta đã sử dụng forkJoin
để đăng ký cả lệnh ordersData
và lệnh getCustomers
. Cũng giống như với $q.all
, đăng ký forkJoin
sẽ trả về một mảng dữ liệu của ta theo thứ tự mà ta đã liệt kê. Vì vậy, data[0]
sẽ là đơn đặt hàng của ta và data[1]
sẽ là khách hàng của ta .
Hãy làm một việc nữa để làm sạch điều này. Ta có thể xóa phụ thuộc $q
khỏi dòng 16 trong mảng $inject
và dòng 167 trong định nghĩa hàm của ta .
Xác nhận ứng dụng đang hoạt động
Hãy xem lại trình duyệt và đảm bảo nó hoạt động. Bạn sẽ thấy rằng ứng dụng của ta biên dịch và tải chính xác, vì vậy hãy kiểm tra tab đơn đặt hàng:
Điều này cho thấy rằng dữ liệu của ta đang tải chính xác. Đến đây bạn đã thấy cách dịch qua lại giữa lời hứa và người có thể quan sát, điều này rất hữu ích khi bạn đang làm việc trên một ứng dụng lớn, nơi bạn không thể chỉ chuyển đổi mọi thứ thành có thể quan sát cùng một lúc khi đang nâng cấp.
Kết luận
Từ đây, hãy sử dụng hướng dẫn này và hướng dẫn cuối cùng để chuyển đổi thành phần customersTable
và lộ trình products
. Bạn cần học một vài thủ thuật mới với cú pháp mẫu của Angular, nhưng nếu không, bạn sẽ có mọi thứ mình cần.
Các tin liên quan