ข้อมูลการตอบสนอง HTTP ที่แคชได้โดยใช้ Rxjs Observer / Observable + Caching + การสมัครสมาชิก
ดูรหัสด้านล่าง
* ข้อจำกัดความรับผิดชอบ: ฉันใหม่สำหรับ rxjs ดังนั้นโปรดจำไว้ว่าฉันอาจใช้วิธีการสังเกตการณ์ / ผู้สังเกตการณ์ในทางที่ผิด โซลูชันของฉันคือการรวมกลุ่มของโซลูชันอื่นที่ฉันพบอย่างหมดจดและเป็นผลมาจากการล้มเหลวในการค้นหาโซลูชันที่มีเอกสารที่เรียบง่าย ดังนั้นฉันจึงนำเสนอโซลูชันรหัสที่สมบูรณ์ของฉัน (ตามที่ฉันต้องการจะพบ) โดยหวังว่าจะช่วยให้ผู้อื่น
* หมายเหตุวิธีนี้ขึ้นอยู่กับ GoogleFirebaseObservables น่าเสียดายที่ฉันไม่มีประสบการณ์ / เวลาที่เหมาะสมในการทำซ้ำสิ่งที่พวกเขาทำภายใต้ประทุน แต่ต่อไปนี้เป็นวิธีที่ง่ายในการจัดเตรียมการเข้าถึงแบบอะซิงโครนัสกับข้อมูลแคชบางตัว
สถานการณ์ : คอมโพเนนต์ 'รายการผลิตภัณฑ์' ได้รับมอบหมายพร้อมแสดงรายการผลิตภัณฑ์ ไซต์ดังกล่าวเป็นเว็บแอปเดียวที่มีปุ่มเมนูบางปุ่มที่จะ 'กรอง' ผลิตภัณฑ์ที่แสดงบนหน้า
การแก้ไข : ส่วนประกอบ "สมัครสมาชิก" กับวิธีการบริการ วิธีการบริการส่งกลับอาร์เรย์ของวัตถุผลิตภัณฑ์ซึ่งส่วนประกอบเข้าถึงผ่านการเรียกกลับการสมัครสมาชิก วิธีการบริการล้อมรอบกิจกรรมใน Observer ที่สร้างขึ้นใหม่และส่งคืนผู้สังเกตการณ์ ภายในผู้สังเกตการณ์นี้มันจะค้นหาข้อมูลที่เก็บไว้และส่งกลับไปยังสมาชิก (ส่วนประกอบ) และส่งกลับ มิเช่นนั้นจะมีการเรียก http เพื่อดึงข้อมูลสมัครรับการตอบกลับซึ่งคุณสามารถประมวลผลข้อมูลนั้น (เช่นแมปข้อมูลกับรุ่นของคุณเอง) แล้วส่งข้อมูลกลับไปยังผู้สมัครสมาชิก
รหัส
ผลิตภัณฑ์ 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 จะถูกเก็บไว้ในระหว่างการนำทางและการเรียก "GetProducts ()" ในภายหลังจะส่งคืนค่าแคช หนึ่งในบันทึกสุดท้ายฉันได้อ่านความคิดเห็นเกี่ยวกับวิธีที่ต้องปิดการติดตาม / สมัครสมาชิกเมื่อคุณทำเสร็จแล้วเพื่อป้องกัน 'หน่วยความจำรั่ว' ฉันไม่ได้รวมสิ่งนี้ไว้ที่นี่ แต่เป็นสิ่งที่ควรคำนึงถึง