puddlejumper26 / SportStore

0 stars 0 forks source link

Stage 1 All codes - All basics #1

Open puddlejumper26 opened 3 years ago

puddlejumper26 commented 3 years ago

Structure

image

puddlejumper26 commented 3 years ago

Model / model.module.ts

import { NgModule } from '@angular/core';
import { ProductRepository } from './product.repository';
import { StaticDataSource } from './static.datasource';

@NgModule({
    providers: [ProductRepository, StaticDataSource]
})
export class ModelModule { }
puddlejumper26 commented 3 years ago

Model / product.model.ts

export class Product{
    constructor(
        public id?: number,
        public name?: string,
        public category?: string,
        public description?: string,
        public price?: number) {}
}
puddlejumper26 commented 3 years ago

Model / product.repository.ts

import { Injectable } from '@angular/core';
import { Product } from './product.model';
import { StaticDataSource } from './static.datasource';

@Injectable()
export class ProductRepository {
  private products: Product[] = [];
  private categories: string[] = [];

  constructor(private dataSource: StaticDataSource) {
    dataSource.getProducts().subscribe((data) => {
      this.products = data;
      this.categories = data
        .map((p) => p.category)
        .filter((c, index, array) => array.indexOf(c) === index)
        .sort();
    });
  }

  getProducts(category: string = null): Product[] {

    // console.log('ProductReposity - products.filter ->', this.products.filter(
    //   (p) => category == null || category === p.category
    // ));

    return this.products.filter(
      (p) => category == null || category === p.category
    );
  }

  getProduct(id: number): Product {
    return this.products.find((p) => p.id === id);
  }

  getCategories(): string[] {

    // console.log('ProductReposity - categories -> ', this.categories);

    return this.categories;
  }
}

  // console.log('ProductReposity - products.filter ->', this.products.filter(
  //   (p) => category == null || category == p.category
  // ));
  // ->
  // (15) [Product, Product, Product, Product, Product, Product, Product, Product, Product, Product, Product, Product, Product, Product, Product]
  // 0: Product {id: 1, name: "Product 1", category: "Category 1", description: "Product 1 (Category 1)", price: 100}
  // 1: Product {id: 2, name: "Product 2", category: "Category 1", description: "Product 2 (Category 1)", price: 100}
  // 2: Product {id: 3, name: "Product 3", category: "Category 1", description: "Product 3 (Category 1)", price: 100}
  // 3: Product {id: 4, name: "Product 4", category: "Category 1", description: "Product 4 (Category 1)", price: 100}
  // 4: Product {id: 5, name: "Product 5", category: "Category 1", description: "Product 5 (Category 1)", price: 100}
  // 5: Product {id: 6, name: "Product 6", category: "Category 2", description: "Product 6 (Category 2)", price: 100}
  // 6: Product {id: 7, name: "Product 7", category: "Category 2", description: "Product 7 (Category 2)", price: 100}
  // 7: Product {id: 8, name: "Product 8", category: "Category 2", description: "Product 8 (Category 2)", price: 100}
  // 8: Product {id: 9, name: "Product 9", category: "Category 2", description: "Product 9 (Category 2)", price: 100}
  // 9: Product {id: 10, name: "Product 10", category: "Category 2", description: "Product 10 (Category 2)", price: 100}
  // 10: Product {id: 11, name: "Product 11", category: "Category 3", description: "Product 11 (Category 3)", price: 100}
  // 11: Product {id: 12, name: "Product 12", category: "Category 3", description: "Product 12 (Category 3)", price: 100}
  // 12: Product {id: 13, name: "Product 13", category: "Category 3", description: "Product 13 (Category 3)", price: 100}
  // 13: Product {id: 14, name: "Product 14", category: "Category 3", description: "Product 14 (Category 3)", price: 100}
  // 14: Product {id: 15, name: "Product 15", category: "Category 3", description: "Product 15 (Category 3)", price: 100}
  // length: 15

  // if click 'Category 1' then
  // ->
  // (5) [Product, Product, Product, Product, Product]
  // 0: Product {id: 1, name: "Product 1", category: "Category 1", description: "Product 1 (Category 1)", price: 100}
  // 1: Product {id: 2, name: "Product 2", category: "Category 1", description: "Product 2 (Category 1)", price: 100}
  // 2: Product {id: 3, name: "Product 3", category: "Category 1", description: "Product 3 (Category 1)", price: 100}
  // 3: Product {id: 4, name: "Product 4", category: "Category 1", description: "Product 4 (Category 1)", price: 100}
  // 4: Product {id: 5, name: "Product 5", category: "Category 1", description: "Product 5 (Category 1)", price: 100}
  // length: 5

  // console.log('ProductReposity - categories -> ', this.categories);
  // ->
  // (3) ["Category 1", "Category 2", "Category 3"]
  // 0: "Category 1"
  // 1: "Category 2"
  // 2: "Category 3"
  // length: 3

  // ********************************* */
  // 解释这里 的 map 和 filter 的作用
  // ********************************* */

  // let arr = [
  //   {
  //     id: 3,
  //     name: "Product 3",
  //     category: "Category 1",
  //     description: "Product 3 (Category 1)",
  //     price: 100
  //   },
  //   {
  //     id: 1,
  //     name: "Product 1",
  //     category: "Category 1",
  //     description: "Product 1 (Category 1)",
  //     price: 100
  //   },
  //   {
  //     id: 4,
  //     name: "Product 4",
  //     category: "Category 4",
  //     description: "Product 4 (Category 4)",
  //     price: 100
  //   },
  //   {
  //     id: 2,
  //     name: "Product 2",
  //     category: "Category 2",
  //     description: "Product 2 (Category 2)",
  //     price: 100
  //   }
  // ];

  // let data = arr.map((p) => p.category);
  // console.log("data -> ", data);

  // ["Category 1", "Category 1", "Category 4", "Category 2"]
  // 0: "Category 1"
  // 1: "Category 1"
  // 2: "Category 4"
  // 3: "Category 2"

  // console.log(data.indexOf('Category 3')); //2
  // let res = data.filter((c, index, array) => array.indexOf(c) === index);
  // console.log(res);

  // indexOf()方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。
  // 所以这里res 变成 三个元素组成的数组

  // ["Category 1", "Category 4", "Category 2"]
  // 0: "Category 1"
  // 1: "Category 4"
  // 2: "Category 2"

  // console.log(res.sort());
  // ["Category 1", "Category 2", "Category 4"]
  // 0: "Category 1"
  // 1: "Category 2"
  // 2: "Category 4"
puddlejumper26 commented 3 years ago

Model / static.datasource.ts

import { Injectable } from '@angular/core';
import { Product } from './product.model';
import { Observable, from } from 'rxjs';

@Injectable()
export class StaticDataSource {
    private products: Product[] = [
        new Product(1, 'Product 1', 'Category 1', 'Product 1 (Category 1)', 100),
        new Product(2, 'Product 2', 'Category 1', 'Product 2 (Category 1)', 100),
        new Product(3, 'Product 3', 'Category 1', 'Product 3 (Category 1)', 100),
        new Product(4, 'Product 4', 'Category 1', 'Product 4 (Category 1)', 100),
        new Product(5, 'Product 5', 'Category 1', 'Product 5 (Category 1)', 100),
        new Product(6, 'Product 6', 'Category 2', 'Product 6 (Category 2)', 100),
        new Product(7, 'Product 7', 'Category 2', 'Product 7 (Category 2)', 100),
        new Product(8, 'Product 8', 'Category 2', 'Product 8 (Category 2)', 100),
        new Product(9, 'Product 9', 'Category 2', 'Product 9 (Category 2)', 100),
        new Product(10, 'Product 10', 'Category 2', 'Product 10 (Category 2)', 100),
        new Product(11, 'Product 11', 'Category 3', 'Product 11 (Category 3)', 100),
        new Product(12, 'Product 12', 'Category 3', 'Product 12 (Category 3)', 100),
        new Product(13, 'Product 13', 'Category 3', 'Product 13 (Category 3)', 100),
        new Product(14, 'Product 14', 'Category 3', 'Product 14 (Category 3)', 100),
        new Product(15, 'Product 15', 'Category 3', 'Product 15 (Category 3)', 100),
    ];

    getProducts(): Observable<Product[]> {
        return from([this.products]);
    }
}
puddlejumper26 commented 3 years ago

Store / counter.directive.ts

import {
    Directive, ViewContainerRef, TemplateRef, Input, Attribute, SimpleChanges
} from "@angular/core";

@Directive(
    {
        selector: '[counterOf]'
    }
)
export class CounterDirective {
    constructor(private container: ViewContainerRef,
        private template: TemplateRef<Object>){
    }

    @Input('counterOf')
    countera: number;

    ngOnChanges(changes: SimpleChanges){
        this.container.clear();
        for(let i=0; i<this.countera;i++){
            this.container.createEmbeddedView(this.template, new CounterDirectiveContext(i+1));
        }
    }
}

class CounterDirectiveContext {
    constructor(public $implicit: any){}
}
puddlejumper26 commented 3 years ago

Store / store.component.html

<div class="container-fluid">
  <div class="row">
    <div class="col bg-dark text-white">
      <a class="navbar-brand">SPORTS STORE</a>
    </div>
  </div>

  <div class="row">
    <div class="col-3 p-2">
      <button
        class="btn btn-block btn-outline-primary"
        (click)="changeCategory()"
      >
        Home
      </button>

      <button
        *ngFor="let cat of categories"
        class="btn btn-outline-primary btn-block"
        [class.active]="cat == selectedCategory"
        (click)="changeCategory(cat)"
      >
        {{ cat }}
      </button>
    </div>

    <div class="col-9 p-2">
      <div *ngFor="let product of products" class="card m-1 p-1 bg-light">
        <h4>
          {{ product.name }}
          <span class="badge badge-pill badge-primary float-right">
            {{ product.price | currency: "USD":"symbol":"2.2-2" }}
          </span>
        </h4>
        <div class="card-text bg-white p-1">{{ product.description }}</div>
      </div>

      <div class="form-inline float-left mr-1">
        <select
          class="form-control"
          (change)="changePageSize($event.target.value)"
        >
          <option value="3">3 per Page</option>
          <option value="4" selected>4 per Page</option>
          <option value="6">6 per Page</option>
          <option value="8">8 per Page</option>
        </select>
      </div>

      <div class="btn-group float-right">
        <button
          *counter ="let page of pageCount"
          (click)="changePage(page)"
          class="btn btn-outline-primary"
          [class.active]="page === selectedPage"
        >
          {{ page }}
        </button>
      </div>
    </div>
  </div>
</div>
puddlejumper26 commented 3 years ago

Store / store.component.ts

import { Component } from '@angular/core';
import { Product } from '../model/product.model';
import { ProductRepository } from '../model/product.repository';

@Component({
  selector: 'store',
  templateUrl: 'store.component.html',
})
export class StoreComponent {
  public selectedCategory = null;

  public productsPerPage = 4;
  public selectedPage = 1;

  constructor(private repository: ProductRepository) {}

  // 根据this.selectedCategory的值(如Category 1)来生成新的数组,如果点击Category 1的按钮,则生成下列数组
  // [Product, Product, Product, Product, Product]
  // 0: Product {id: 1, name: "Product 1", category: "Category 1", description: "Product 1 (Category 1)", price: 100}
  // 1: Product {id: 2, name: "Product 2", category: "Category 1", description: "Product 2 (Category 1)", price: 100}
  // 2: Product {id: 3, name: "Product 3", category: "Category 1", description: "Product 3 (Category 1)", price: 100}
  // 3: Product {id: 4, name: "Product 4", category: "Category 1", description: "Product 4 (Category 1)", price: 100}
  // 4: Product {id: 5, name: "Product 5", category: "Category 1", description: "Product 5 (Category 1)", price: 100}
  // length: 5
  get products(): Product[] {
    // console.log('this.selectedCategory -> ', this.selectedCategory);
    // console.log('this.repository.getProducts(this.selectedCategory) -> ', this.repository.getProducts(this.selectedCategory));
    const pageIndex = (this.selectedPage - 1) * this.productsPerPage;
    return this.repository
    .getProducts(this.selectedCategory)
    .slice(pageIndex, pageIndex + this.productsPerPage);
  }

  get categories(): string[] {
    // console.log('this.repository.getCategories() -> ', this.repository.getCategories());
    return this.repository.getCategories();
  }

  changeCategory(newCategory?: string) {
    // console.log('newCategory -> ', newCategory)
    this.selectedCategory = newCategory;
  }

  changePage(newPage: number) {
    this.selectedPage = newPage;
    // console.log(this.selectedPage);
  }

  changePageSize(newSize: number) {
    this.productsPerPage = Number(newSize);
    this.selectedPage = 1;
  }

  get pageCount():number{
    return Math.ceil(this.repository.getProducts(this.selectedCategory).length/this.productsPerPage)
  }

  get pageNumbers(): number[] {
    return Array(
      Math.ceil(
        this.repository.getProducts(this.selectedCategory).length /
          this.productsPerPage
      )
    )
      .fill(0)
      .map((x, i) => i + 1);
  }
}

  // console.log('this.selectedCategory -> ', this.selectedCategory);
  // ->

  // console.log('this.repository.getCategories() -> ', this.repository.getCategories());
  // ->
  // ["Category 1", "Category 2", "Category 3"]
  // 0: "Category 1"
  // 1: "Category 2"
  // 2: "Category 3"
  // length: 3

  // console.log('newCategory -> ', newCategory)
  // -> newCategory 和 selectedCategory 一直都是一样的,因为这一步将其赋值给了selectedCategory
  // 所以这里如果点击 Category 1 的按钮,则两者都显示 Category 1
puddlejumper26 commented 3 years ago

Store / store.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { ModelModule } from '../model/model.module';
import { StoreComponent } from './store.component';
import { CounterDirective } from './counter.directive';

@NgModule({
  imports: [ModelModule, BrowserModule, FormsModule],
  declarations: [StoreComponent,CounterDirective],
  exports: [StoreComponent],
})
export class StoreModule {}
puddlejumper26 commented 3 years ago

app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }
puddlejumper26 commented 3 years ago

app.component.html

<store></store>
puddlejumper26 commented 3 years ago

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'SportStore';
}
puddlejumper26 commented 3 years ago

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { StoreModule } from './store/store.module';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    AppRoutingModule,
    BrowserModule,
    StoreModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
puddlejumper26 commented 3 years ago

authMiddleware.js

const jwt = require("jsonwebtoken");

const APP_SECRET = "myappsecret";
const USERNAME = "admin";
const PASSWORD = "secret";

module.exports = function ( req, res, next ) {
    if ( (req.url == "/api/login" || req.url == "/login") && req.method == "POST" ){
        if ( req.body != null && req.body.name == USERNAME && req.body.password == PASSWORD ) {
            let token = jwt.sign ({data: USERNAME, expiresIn: "1h"}, APP_SECRET);
            res.json ({success:true, token: token});
        } else {
            res.json({success: true});
        }
        res.end();
        return;
    } else if ((((req.url.startsWith("/api/products")
            || req.url.startsWith ("/products")
            || req.url.startsWith ("/api/categories")
            || req.url.startsWith ("/categories"))) && req.method != "GET")
            || ((req.url.startsWith ("/api/orders")
            || req.url.startsWith ("/orders")) && req.method != "POST")) {
                let token = req.headers["authorization"];
                if(token != null && token.startsWith ("Bearer<")) {
                    token = token.substring(7, token.length - 1);
                    try {
                            jwt.verify(token,APP_SECRET);
                            next();
                            return;
                        } catch (err) {}
                }
                res.statusCode = 401;
                res.end();
                return;
            }
            next();
}

// const jwt = require("jsonwebtoken");

// const APP_SECRET = "myappsecret";
// const USERNAME = "admin";
// const PASSWORD = "secret";

// module.exports = function (req, res, next) {

//     if ((req.url == "/api/login" || req.url == "/login")
//             && req.method == "POST") {
//         if (req.body != null && req.body.name == USERNAME
//                 && req.body.password == PASSWORD) {
//             let token = jwt.sign({ data: USERNAME, expiresIn: "1h" }, APP_SECRET);
//             res.json({ success: true, token: token });
//         } else {
//             res.json({ success: false });
//         }
//         res.end();
//         return;
//     } else if ((((req.url.startsWith("/api/products")
//                 || req.url.startsWith("/products"))
//            || (req.url.startsWith("/api/categories")
//                 || req.url.startsWith("/categories"))) && req.method != "GET")
//         || ((req.url.startsWith("/api/orders")
//             || req.url.startsWith("/orders")) && req.method != "POST")) {
//         let token = req.headers["authorization"];
//         if (token != null && token.startsWith("Bearer<")) {
//             token = token.substring(7, token.length - 1);
//             try {
//                 jwt.verify(token, APP_SECRET);
//                 next();
//                 return;
//             } catch (err) { }
//         }
//         res.statusCode = 401;
//         res.end();
//         return;
//     }
//     next();
// }
puddlejumper26 commented 3 years ago

data.js

module.exports = function () {
    return {
        products: [
            { id: 1, name: "Kayak", category: "Watersports", description: "A boat for one person", price: 275 },
            { id: 2, name: "Lifejacket", category: "Watersports", description: "Protective and fashionable", price: 48.95 },
            { id: 3, name: "Soccer Ball", category: "Soccer", description: "FIFA-approved size and weight", price: 19.50 },
            { id: 4, name: "Corner Flag", category: "Soccer", description: "Give your playing field a professional touch", price: 34.95 },
            { id: 5, name: "Stadium", category: "Soccer", description: "Flat-packed 35,000-seat stadium", price: 79500 },
            { id: 6, name: "Thinking Cap", category: "Chess", description: "Improve brain efficiency by 75%" , price: 16 },
            { id: 7, name: "Unsteady Chair", category: "Chess", description: "Secretly give your opponent a disadvantage" , price: 29.95},
            { id: 8, name: "Human Chess Board", category: "Chess", description: "A fun game for the family" , price: 75 },
            { id: 9, name: "Bling Bling King", category: "Chess", description: "Glod-plated, diamond-studded King" , price: 1200},
        ],
        orders: []
    }
}