Completed Cache

This commit is contained in:
Charles Showalter 2022-05-31 17:15:00 -07:00
parent f6735a38fe
commit d875c033d9
19 changed files with 153 additions and 77 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View File

@ -1,26 +1,26 @@
[ [
{ {
"Id": 1, "Id": 1,
"Name": "Angular" "Name": "ABS"
}, },
{ {
"Id": 2, "Id": 2,
"Name": "NetCore" "Name": "Cyberpower"
}, },
{ {
"Id": 3, "Id": 3,
"Name": "VS Code" "Name": "Skytech"
}, },
{ {
"Id": 4, "Id": 4,
"Name": "React" "Name": "Velztorm"
}, },
{ {
"Id": 5, "Id": 5,
"Name": "Typescript" "Name": "Bose"
}, },
{ {
"Id": 6, "Id": 6,
"Name": "Redis" "Name": "Beats"
} }
] ]

View File

@ -1,51 +1,51 @@
[ [
{ {
"Name": "Angular Speedster Board 2000", "Name": "ABS Gladiator Gaming PC",
"Description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis urna.", "Description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis urna.",
"Price": 200, "Price": 1749,
"PictureUrl": "images/products/sb-ang1.png", "PictureUrl": "images/products/ABS-Gladiator-Gaming-PC-1749.jpg",
"ProductTypeId": 1, "ProductTypeId": 1,
"ProductBrandId": 1 "ProductBrandId": 1
}, },
{ {
"Name": "Green Angular Board 3000", "Name": "CyberpowerPC Gaming Desktop Gamer Master",
"Description": "Nunc viverra imperdiet enim. Fusce est. Vivamus a tellus.", "Description": "Nunc viverra imperdiet enim. Fusce est. Vivamus a tellus.",
"Price": 150, "Price": 1699,
"PictureUrl": "images/products/sb-ang2.png", "PictureUrl": "images/products/CyberpowerPC-Gaming-Desktop-Gamer-Master-1699.jpg",
"ProductTypeId": 1, "ProductTypeId": 1,
"ProductBrandId": 1 "ProductBrandId": 2
}, },
{ {
"Name": "Core Board Speed Rush 3", "Name": "Skytech Archangel 3.0 Gaming Computer PC Desktop",
"Description": "Suspendisse dui purus, scelerisque at, vulputate vitae, pretium mattis, nunc. Mauris eget neque at sem venenatis eleifend. Ut nonummy.", "Description": "Suspendisse dui purus, scelerisque at, vulputate vitae, pretium mattis, nunc. Mauris eget neque at sem venenatis eleifend. Ut nonummy.",
"Price": 180, "Price": 1199,
"PictureUrl": "images/products/sb-core1.png", "PictureUrl": "images/products/Skytech-Archangel-3.0-Gaming-Computer-PC-Desktop-1199.jpg",
"ProductTypeId": 1, "ProductTypeId": 1,
"ProductBrandId": 2 "ProductBrandId": 3
}, },
{ {
"Name": "Net Core Super Board", "Name": "Velztorm Nix Custom Built Gaming Desktop PC",
"Description": "Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Proin pharetra nonummy pede. Mauris et orci.", "Description": "Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Proin pharetra nonummy pede. Mauris et orci.",
"Price": 300, "Price": 1449,
"PictureUrl": "images/products/sb-core2.png", "PictureUrl": "images/products/Velztorm-Nix-Custom-Built-Gaming-Desktop-PC-1449.jpg",
"ProductTypeId": 1,
"ProductBrandId": 2
},
{
"Name": "React Board Super Whizzy Fast",
"Description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis urna.",
"Price": 250,
"PictureUrl": "images/products/sb-react1.png",
"ProductTypeId": 1, "ProductTypeId": 1,
"ProductBrandId": 4 "ProductBrandId": 4
}, },
{ {
"Name": "Typescript Entry Board", "Name": "ABS Legend Gaming PC - Intel i9 12900K",
"Description": "Aenean nec lorem. In porttitor. Donec laoreet nonummy augue.", "Description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis urna.",
"Price": 120, "Price": 4149,
"PictureUrl": "images/products/sb-ts1.png", "PictureUrl": "images/products/ABS-Legend-Gaming-PC-Intel-i9-12900K.jpg",
"ProductTypeId": 1, "ProductTypeId": 1,
"ProductBrandId": 5 "ProductBrandId": 1
},
{
"Name": "Skytech Chronos Gaming PC Desktop",
"Description": "Aenean nec lorem. In porttitor. Donec laoreet nonummy augue.",
"Price": 1599,
"PictureUrl": "images/products/Skytech-Chronos-Gaming-PC-Desktop.jpg",
"ProductTypeId": 1,
"ProductBrandId": 3
}, },
{ {
"Name": "Core Blue Hat", "Name": "Core Blue Hat",

View File

@ -1,18 +1,18 @@
[ [
{ {
"Id": 1, "Id": 1,
"Name": "Boards" "Name": "Computers"
}, },
{ {
"Id": 2, "Id": 2,
"Name": "Hats" "Name": "Headphones"
}, },
{ {
"Id": 3, "Id": 3,
"Name": "Boots" "Name": "Gadgets"
}, },
{ {
"Id": 4, "Id": 4,
"Name": "Gloves" "Name": "Accessories"
} }
] ]

View File

@ -1,3 +1,3 @@
# SkiNet # SkyNet
Built upon ASP.NET, Angular, Bootstrap, and Stripe for Credit Card Processing! Built upon ASP.NET, Angular, Bootstrap, and Stripe for Credit Card Processing!

View File

@ -8,7 +8,7 @@ import { AccountService } from './account/account.service';
styleUrls: ['./app.component.scss'] styleUrls: ['./app.component.scss']
}) })
export class AppComponent implements OnInit { export class AppComponent implements OnInit {
title = 'SkiNet'; title = 'SkyNet';
constructor(private basketService: BasketService, private accountService: AccountService) {} constructor(private basketService: BasketService, private accountService: AccountService) {}

View File

@ -2,6 +2,7 @@
[boundaryLinks]="true" [boundaryLinks]="true"
[totalItems]="totalCount" [totalItems]="totalCount"
(pageChanged)="onPagerChange($event)" (pageChanged)="onPagerChange($event)"
[ngModel]="pageNumber"
[itemsPerPage]="pageSize" [itemsPerPage]="pageSize"
previousText="‹" previousText="‹"
nextText="›" nextText="›"

View File

@ -8,6 +8,7 @@ import { Component, EventEmitter, Input, Output, OnInit } from '@angular/core';
export class PagerComponent implements OnInit { export class PagerComponent implements OnInit {
@Input() totalCount: number; @Input() totalCount: number;
@Input() pageSize: number; @Input() pageSize: number;
@Input() pageNumber: number;
@Output() pageChanged = new EventEmitter<number>(); @Output() pageChanged = new EventEmitter<number>();
constructor() { } constructor() { }

View File

@ -1,8 +1,15 @@
import { IProduct } from "./product" import { IProduct } from "./product"
export interface IPagination { export interface IPagination {
pageIndex: number pageIndex: number;
pageSize: number pageSize: number;
count: number count: number;
data: IProduct[] data: IProduct[];
}
export class Pagination implements IPagination {
pageIndex: number;
pageSize: number;
count: number;
data: IProduct[] = [];
} }

View File

@ -29,6 +29,7 @@ import { RouterModule } from '@angular/router';
CarouselModule.forRoot(), CarouselModule.forRoot(),
BsDropdownModule.forRoot(), BsDropdownModule.forRoot(),
ReactiveFormsModule, ReactiveFormsModule,
FormsModule,
CdkStepperModule, CdkStepperModule,
RouterModule RouterModule
], ],
@ -40,6 +41,7 @@ import { RouterModule } from '@angular/router';
OrderTotalsComponent, OrderTotalsComponent,
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
FormsModule,
BsDropdownModule, BsDropdownModule,
TextInputsComponent, TextInputsComponent,
CdkStepperModule, CdkStepperModule,

View File

@ -5,6 +5,7 @@
<h5 class="text-warning ml-3">Sort</h5> <h5 class="text-warning ml-3">Sort</h5>
<select class="form-select mb-3" style="width: 100%;" (change)="onSortSelected($event.target.value)"> <select class="form-select mb-3" style="width: 100%;" (change)="onSortSelected($event.target.value)">
<option *ngFor="let sort of sortOptions" <option *ngFor="let sort of sortOptions"
[selected]="shopParams.sort === sort.value"
[value]="sort.value" [value]="sort.value"
> >
{{sort.name}} {{sort.name}}
@ -57,6 +58,7 @@
<app-pager <app-pager
[pageSize]="shopParams.pageSize" [pageSize]="shopParams.pageSize"
[totalCount]="totalCount" [totalCount]="totalCount"
[pageNumber]="shopParams.pageNumber"
(pageChanged)="onPageChange($event)" (pageChanged)="onPageChange($event)"
></app-pager> ></app-pager>
</div> </div>

View File

@ -15,7 +15,7 @@ export class ShopComponent implements OnInit {
products: IProduct[]; products: IProduct[];
brands: IBrand[]; brands: IBrand[];
productType: IType[]; productType: IType[];
shopParams = new ShopParams(); shopParams: ShopParams;
totalCount: number; totalCount: number;
sortOptions = [ sortOptions = [
{name: 'Name', value: 'name'}, {name: 'Name', value: 'name'},
@ -23,21 +23,21 @@ export class ShopComponent implements OnInit {
{name: 'Price: High to Low', value: 'priceDesc'} {name: 'Price: High to Low', value: 'priceDesc'}
]; ];
constructor(private shopService: ShopService) { } constructor(private shopService: ShopService) {
this.shopParams = this.shopService.getShopParams();
}
ngOnInit(): void { ngOnInit(): void {
this.getProducts(); this.getProducts(true);
this.getBrands(); this.getBrands();
this.getTypes(); this.getTypes();
} }
getProducts(){ getProducts(useCache = false){
this.shopService.getProducts(this.shopParams).subscribe( this.shopService.getProducts(useCache).subscribe(
{ {
next: (response) => { next: (response) => {
this.products = response.data; this.products = response.data;
this.shopParams.pageNumber = response.pageIndex;
this.shopParams.pageSize = response.pageSize;
this.totalCount = response.count; this.totalCount = response.count;
}, },
error: (e: any) => { console.log(e); }, error: (e: any) => { console.log(e); },
@ -67,38 +67,49 @@ export class ShopComponent implements OnInit {
} }
onBrandSelected(brandId: number){ onBrandSelected(brandId: number){
this.shopParams.brandId = brandId; const params = this.shopService.getShopParams();
this.shopParams.pageNumber = 1; params.brandId = brandId;
params.pageNumber = 1;
this.shopService.setShopParams(params);
this.getProducts(); this.getProducts();
} }
onTypeSelected(typeId: number){ onTypeSelected(typeId: number){
this.shopParams.typeId = typeId; const params = this.shopService.getShopParams();
this.shopParams.pageNumber = 1; params.typeId = typeId;
params.pageNumber = 1;
this.shopService.setShopParams(params);
this.getProducts(); this.getProducts();
} }
onSortSelected(sort: string){ onSortSelected(sort: string){
this.shopParams.sort = sort; const params = this.shopService.getShopParams();
params.sort = sort;
this.shopService.setShopParams(params);
this.getProducts(); this.getProducts();
} }
onPageChange(e: any){ onPageChange(e: any){
if (this.shopParams.pageNumber !== e){ const params = this.shopService.getShopParams();
this.shopParams.pageNumber = e; if (params.pageNumber !== e){
this.getProducts(); params.pageNumber = e;
this.shopService.setShopParams(params);
this.getProducts(true);
} }
} }
onSearch(){ onSearch(){
this.shopParams.search = this.searchTerm.nativeElement.value; const params = this.shopService.getShopParams()
this.shopParams.pageNumber = 1; params.search = this.searchTerm.nativeElement.value;
params.pageNumber = 1;
this.shopService.setShopParams(params);
this.getProducts(); this.getProducts();
} }
OnReset(){ OnReset(){
this.searchTerm.nativeElement.value = ''; this.searchTerm.nativeElement.value = '';
this.shopParams = new ShopParams(); this.shopParams = new ShopParams();
this.shopService.setShopParams(this.shopParams);
this.getProducts(); this.getProducts();
} }

View File

@ -1,56 +1,108 @@
import { HttpClient, HttpParams } from '@angular/common/http'; import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { IBrand } from '../shared/models/brand'; import { IBrand } from '../shared/models/brand';
import { IPagination } from '../shared/models/pagination'; import { IPagination, Pagination } from '../shared/models/pagination';
import { IType } from '../shared/models/producttype'; import { IType } from '../shared/models/producttype';
import { map } from 'rxjs/operators' import { map } from 'rxjs/operators'
import { ShopParams } from '../shared/models/shopparams'; import { ShopParams } from '../shared/models/shopparams';
import { IProduct } from '../shared/models/product'; import { IProduct } from '../shared/models/product';
import { of } from 'rxjs';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class ShopService { export class ShopService {
baseURL = 'https://localhost:5001/api/' baseURL = 'https://localhost:5001/api/';
products: IProduct[] = [];
brands: IBrand[] = [];
types: IType[] = [];
pagination = new Pagination();
shopParams = new ShopParams();
productCache = new Map();
constructor(private http: HttpClient) { } constructor(private http: HttpClient) { }
getProducts(shopParams: ShopParams){ getProducts(useCache: boolean){
if (useCache === false){
this.productCache = new Map();
}
if(this.productCache.size > 0 && useCache === true){
if(this.productCache.has(Object.values(this.shopParams).join('-'))) {
this.pagination.data = this.productCache.get(Object.values(this.shopParams).join('-'));
return of(this.pagination);
}
}
let params = new HttpParams(); let params = new HttpParams();
if (shopParams.brandId !== 0){ if (this.shopParams.brandId !== 0){
params = params.append('brandId', shopParams.brandId.toString()); params = params.append('brandId', this.shopParams.brandId.toString());
} }
if(shopParams.typeId !== 0){ if(this.shopParams.typeId !== 0){
params = params.append('typeId', shopParams.typeId.toString()); params = params.append('typeId', this.shopParams.typeId.toString());
} }
if (shopParams.search){ if (this.shopParams.search){
params = params.append('search', shopParams.search); params = params.append('search', this.shopParams.search);
} }
params = params.append('sort', shopParams.sort); params = params.append('sort', this.shopParams.sort);
params = params.append('pageIndex', shopParams.pageNumber.toString()); params = params.append('pageIndex', this.shopParams.pageNumber.toString());
params = params.append('pageIndex', shopParams.pageSize.toString()); params = params.append('pageIndex', this.shopParams.pageSize.toString());
return this.http.get<IPagination>(this.baseURL + 'products', {observe: 'response', params}) return this.http.get<IPagination>(this.baseURL + 'products', {observe: 'response', params})
.pipe( .pipe(
map(response => { map(response => {
return response.body; this.productCache.set(Object.values(this.shopParams).join('-'), response.body.data);
this.pagination = response.body;
return this.pagination;
}) })
); );
} }
setShopParams(params: ShopParams){
this.shopParams = params;
}
getShopParams(){
return this.shopParams;
}
getProduct(id: number){ getProduct(id: number){
let product: IProduct;
this.productCache.forEach((products: IProduct[]) => {
product = products.find(p => p.id === id);
})
if(product){
return of(product);
}
return this.http.get<IProduct>(this.baseURL + 'products/' + id); return this.http.get<IProduct>(this.baseURL + 'products/' + id);
} }
getBrands(){ getBrands(){
return this.http.get<IBrand[]>(this.baseURL + 'products/brands'); if(this.brands.length > 0){
return of(this.brands);
}
return this.http.get<IBrand[]>(this.baseURL + 'products/brands').pipe(
map(response => {
this.brands = response;
return response;
})
);
} }
getTypes(){ getTypes(){
return this.http.get<IType[]>(this.baseURL + 'products/types'); if(this.types.length > 0){
return of(this.types);
}
return this.http.get<IType[]>(this.baseURL + 'products/types').pipe(
map(response => {
this.types = response;
return response;
})
);
} }
} }

View File

@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>SkiNet</title> <title>SkyNet</title>
<base href="/"> <base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="icon" type="image/x-icon" href="favicon.ico">