Data Respons HTTP Cacheable menggunakan Rxjs Observer / Observable + Caching + Subscription
Lihat Kode Di Bawah Ini
* Penafian: Saya baru mengenal rxjs, jadi ingatlah bahwa saya mungkin menyalahgunakan pendekatan observable / observer. Solusi saya murni konglomerasi dari solusi lain yang saya temukan, dan merupakan konsekuensi dari kegagalan untuk menemukan solusi sederhana yang terdokumentasi dengan baik. Jadi saya memberikan solusi kode lengkap saya (seperti yang ingin saya temukan) dengan harapan dapat membantu orang lain.
* perhatikan, pendekatan ini secara longgar didasarkan pada GoogleFirebaseObservables. Sayangnya saya kurang pengalaman / waktu yang tepat untuk meniru apa yang mereka lakukan di bawah tenda. Tetapi berikut ini adalah cara sederhana untuk menyediakan akses tidak sinkron ke beberapa data yang bisa di-cache.
Situasi : Komponen 'daftar produk' bertugas menampilkan daftar produk. Situs ini adalah aplikasi web satu halaman dengan beberapa tombol menu yang akan 'memfilter' produk yang ditampilkan pada halaman.
Solusi : Komponen "berlangganan" ke metode layanan. Metode layanan mengembalikan array objek produk, yang diakses komponen melalui panggilan balik berlangganan. Metode layanan membungkus aktivitasnya dalam Pengamat yang baru dibuat dan mengembalikan pengamat. Di dalam pengamat ini, ia mencari data yang di-cache dan meneruskannya kembali ke pelanggan (komponen) dan kembali. Kalau tidak, ia mengeluarkan panggilan http untuk mengambil data, berlangganan respons, di mana Anda dapat memproses data itu (misalnya memetakan data ke model Anda sendiri) dan kemudian meneruskan data kembali ke pelanggan.
Kode
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 (model)
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...
);
}
}
Ini adalah contoh dari output yang saya lihat ketika saya memuat halaman di Chrome. Perhatikan bahwa pada muatan awal, produk diambil dari http (panggilan ke layanan simpul saya, yang berjalan secara lokal pada port 3000). Ketika saya klik untuk menavigasi ke tampilan 'disaring' dari produk, produk ditemukan dalam cache.
Log Chrome (konsol) saya:
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
... [mengklik tombol menu untuk memfilter produk] ...
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
Kesimpulan: Ini adalah cara paling sederhana yang saya temukan (sejauh ini) untuk mengimplementasikan data respons http yang dapat di-cache. Di aplikasi sudut saya, setiap kali saya menavigasi ke tampilan produk yang berbeda, komponen daftar produk memuat ulang. ProductService tampaknya merupakan contoh bersama, jadi cache lokal 'produk: Produk []' di ProductService dipertahankan selama navigasi, dan panggilan berikutnya ke "GetProducts ()" mengembalikan nilai yang di-cache. Satu catatan terakhir, saya sudah membaca komentar tentang bagaimana observable / langganan perlu ditutup ketika Anda selesai untuk mencegah 'kebocoran memori'. Saya belum memasukkan ini di sini, tapi itu sesuatu yang perlu diingat.