Rxjs Observer / Observable + 캐싱 + 구독을 사용하여 캐시 가능한 HTTP 응답 데이터
아래 코드를 참조하십시오
* 면책 조항 : rxjs를 처음 사용하므로 관찰 / 관찰 방식을 잘못 사용하고있을 수 있습니다. 내 솔루션은 순전히 내가 찾은 다른 솔루션의 대기업이며, 잘 문서화 된 간단한 솔루션을 찾지 못한 결과입니다. 따라서 다른 사람들에게 도움이되기를 바라며 완전한 코드 솔루션을 찾고 있습니다.
*이 접근법은 GoogleFirebaseObservables를 기반으로합니다. 불행히도 나는 그들이 처한 일을 복제 할 적절한 경험 / 시간이 부족합니다. 그러나 다음은 일부 캐시 가능한 데이터에 대한 비동기 액세스를 제공하는 간단한 방법입니다.
상황 : '제품 목록'구성 요소는 제품 목록을 표시하는 작업입니다. 이 사이트는 페이지에 표시된 제품을 '필터링'하는 일부 메뉴 버튼이있는 단일 페이지 웹 앱입니다.
솔루션 : 컴포넌트가 서비스 메소드를 "구독"합니다. 서비스 메소드는 컴포넌트가 구독 콜백을 통해 액세스하는 제품 오브젝트의 배열을 리턴합니다. 서비스 메소드는 새로 작성된 Observer에서 활동을 랩핑하고 관찰자를 리턴합니다. 이 옵저버 내에서 캐시 된 데이터를 검색하여 구독자 (구성 요소)에게 다시 전달하여 반환합니다. 그렇지 않으면 http 호출을 발행하여 데이터를 검색하고 응답을 구독하여 해당 데이터를 처리 (예 : 데이터를 자신의 모델에 맵핑) 한 다음 데이터를 구독자에게 다시 전달할 수 있습니다.
코드
product-list.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { ProductService } from '../../../services/product.service';
import { Product, ProductResponse } from '../../../models/Product';
@Component({
selector: 'app-product-list',
templateUrl: './product-list.component.html',
styleUrls: ['./product-list.component.scss']
})
export class ProductListComponent implements OnInit {
products: Product[];
constructor(
private productService: ProductService
) { }
ngOnInit() {
console.log('product-list init...');
this.productService.getProducts().subscribe(products => {
console.log('product-list received updated products');
this.products = products;
});
}
}
product.service.ts
import { Injectable } from '@angular/core';
import { Http, Headers } from '@angular/http';
import { Observable, Observer } from 'rxjs';
import 'rxjs/add/operator/map';
import { Product, ProductResponse } from '../models/Product';
@Injectable()
export class ProductService {
products: Product[];
constructor(
private http:Http
) {
console.log('product service init. calling http to get products...');
}
getProducts():Observable<Product[]>{
//wrap getProducts around an Observable to make it async.
let productsObservable$ = Observable.create((observer: Observer<Product[]>) => {
//return products if it was previously fetched
if(this.products){
console.log('## returning existing products');
observer.next(this.products);
return observer.complete();
}
//Fetch products from REST API
console.log('** products do not yet exist; fetching from rest api...');
let headers = new Headers();
this.http.get('http://localhost:3000/products/', {headers: headers})
.map(res => res.json()).subscribe((response:ProductResponse) => {
console.log('productResponse: ', response);
let productlist = Product.fromJsonList(response.products); //convert service observable to product[]
this.products = productlist;
observer.next(productlist);
});
});
return productsObservable$;
}
}
product.ts (모델)
export interface ProductResponse {
success: boolean;
msg: string;
products: Product[];
}
export class Product {
product_id: number;
sku: string;
product_title: string;
..etc...
constructor(product_id: number,
sku: string,
product_title: string,
...etc...
){
//typescript will not autoassign the formal parameters to related properties for exported classes.
this.product_id = product_id;
this.sku = sku;
this.product_title = product_title;
...etc...
}
//Class method to convert products within http response to pure array of Product objects.
//Caller: product.service:getProducts()
static fromJsonList(products:any): Product[] {
let mappedArray = products.map(Product.fromJson);
return mappedArray;
}
//add more parameters depending on your database entries and constructor
static fromJson({
product_id,
sku,
product_title,
...etc...
}): Product {
return new Product(
product_id,
sku,
product_title,
...etc...
);
}
}
다음은 Chrome에서 페이지를로드 할 때 표시되는 출력 샘플입니다. 초기로드시 제품은 http (포트 3000에서 로컬로 실행되는 노드 레스트 서비스 호출)에서 가져옵니다. 그런 다음 클릭하여 제품의 '필터링 된'보기로 이동하면 제품이 캐시에 있습니다.
내 Chrome 로그 (콘솔) :
core.es5.js:2925 Angular is running in the development mode. Call enableProdMode() to enable the production mode.
app.component.ts:19 app.component url: /products
product.service.ts:15 product service init. calling http to get products...
product-list.component.ts:18 product-list init...
product.service.ts:29 ** products do not yet exist; fetching from rest api...
product.service.ts:33 productResponse: {success: true, msg: "Products found", products: Array(23)}
product-list.component.ts:20 product-list received updated products
... [제품을 필터링하려면 메뉴 버튼을 클릭했습니다] ...
app.component.ts:19 app.component url: /products/chocolatechip
product-list.component.ts:18 product-list init...
product.service.ts:24 ## returning existing products
product-list.component.ts:20 product-list received updated products
결론 : 이것은 캐시 가능한 http 응답 데이터를 구현하기 위해 지금까지 찾은 가장 간단한 방법입니다. 내 각도 앱에서 제품의 다른보기로 이동할 때마다 제품 목록 구성 요소가 다시로드됩니다. ProductService는 공유 인스턴스 인 것 같으므로 탐색 중에 ProductService에서 'products : Product []'의 로컬 캐시가 유지되며 "GetProducts ()"에 대한 후속 호출은 캐시 된 값을 리턴합니다. 마지막으로, '메모리 누수'를 막기 위해 옵저버 블 / 서브 스크립 션을 닫는 방법에 대한 의견을 읽었습니다. 나는 이것을 여기에 포함시키지 않았지만 명심해야 할 것입니다.