Web应用的创建笔记(2)_增加CRUD API以及前端路由

在上一篇的博文,我们建立了一个简单的WEB应用。现在让我们丰富一下功能,包括以下2个主要方面的改动:

1. 前端增加Routes路由的功能,可以点击跳转到不同的模块。

2. 后端增加了CRUD的功能,可以实现对产品的增加,删除,修改。

先来做后端的改动。对上次创建的ProductController.java文件做改动,增加了显示单个产品详细信息,增加产品,更新产品属性,删除产品这4个API,具体代码如下:

package com.example.myweb;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.CrossOrigin;
import java.util.List;

@RestController
@CrossOrigin(origins ="http://localhost:4200")   //这个用于解决跨域访问的问题
public class ProductController {
    private ProductRepository productRepository;
    @Autowired
    public ProductController(
            ProductRepository productRepository) {
        this.productRepository= productRepository;
    }

    @RequestMapping("/all")
    public List allProducts(Model model) {
        List<Product> productList =
                productRepository.findAll();
        return productList;
    }

    @GetMapping("/productdetail/{id}")
    public Product getProduct(@PathVariable("id") long id) {
        Product product =
                productRepository.getOne(id);
        return product;
    }

    @RequestMapping(value = "/updateproduct", method = RequestMethod.PUT)
    public void updateProduct(
            @RequestBody Product product) {
        Product updatedProduct = new Product();
        updatedProduct.setId(product.getId());
        updatedProduct.setName(product.getName());
        updatedProduct.setDescription(product.getDescription());
        this.productRepository.save(updatedProduct);
    }

    @RequestMapping(value = "/createproduct", method = RequestMethod.PUT)
    public void createProduct(
            @RequestBody Product product) {
        Product newProduct = new Product();
        newProduct.setName(product.getName());
        newProduct.setDescription(product.getDescription());
        this.productRepository.save(newProduct);
    }

    @RequestMapping(value = "/deleteproduct", method = RequestMethod.PUT)
    public void deleteProduct(
            @RequestBody Product product) {
        this.productRepository.deleteById(product.getId());
    }
}

在IDEA中的Maven Projects的Spring boot:run下运行。


对于前端,需要增加路由功能。在my-app/目录下,命令行输入以下命令来创建一个AppRoutingModule:

ng generate module app-routing --flat --module=app

对my-app/src/app/生成的app-routing.module.ts文件做修改,增加routes, 如以下:

import { NgModule }             from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ProductsComponent }      from './products/products.component';
import { ProductDetailComponent }      from './product-detail/product-detail.component';
import { AddProductComponent }      from './add-product/add-product.component';

const routes: Routes = [
  { path: 'products', component: ProductsComponent },
  { path: 'productdetail/:id', component: ProductDetailComponent },
  { path: 'createproduct', component: AddProductComponent }
];

@NgModule({
  imports: [ RouterModule.forRoot(routes) ],
  exports: [ RouterModule ]
})
export class AppRoutingModule {}

文件中的routes就是指向不同component的路径。

对于product.service.ts文件,需要做修改,增加几个新的方法来调用后端新增加的API,如以下代码:

import { Injectable } from '@angular/core';  
import { Observable } from 'rxjs/Observable';  
import { HttpClient, HttpHeaders } from '@angular/common/http';  
import { Product } from './product';  

const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
  
@Injectable()  
export class ProductService {  
  private productsUrl ='http://localhost:9090/all';  //这是REST API的URL  
  private productDetailUrl ='http://localhost:9090/productdetail';
  private productUpdateUrl ='http://localhost:9090/updateproduct';
  private productCreateUrl ='http://localhost:9090/createproduct';
  private productDeleteUrl ='http://localhost:9090/deleteproduct';
  
  constructor(private http:HttpClient) { }  
  
  getProducts(): Observable<Product[]> {  
    return this.http.get<Product[]>(this.productsUrl)  
  }  

  /** GET hero by id. Will 404 if id not found */
  getProduct(id: number): Observable<Product> {
    const url = `${this.productDetailUrl}/${id}`;
    return this.http.get<Product>(url);
  }

  /** PUT: update the product on the server */
  updateProduct (product: Product): Observable<any> {
    return this.http.put(this.productUpdateUrl, product, httpOptions);
  }

  /** PUT: add a new product to the server */
  createProduct (product: Product): Observable<Product> {
    return this.http.put<Product>(this.productCreateUrl, product, httpOptions);
  }

  /** PUT: add a new product to the server */
  deleteProduct (product: Product): Observable<Product> {
    return this.http.put<Product>(this.productDeleteUrl, product, httpOptions);
  }
}

对于products模块的products.component.html,做一下修改,对于生成的Products列表,每一个产品增加一个链接,指向显示product-detail的模块,也就是刚才定义的其中一个route,代码如下:

<h2>Products</h2>  
<ul class="products">  
  <li *ngFor="let product of products">
    <a routerLink="/productdetail/{{product.id}}"> 
      <span class="badge">{{product.id}}</span> {{product.name}}  
    </a>
  </li>  
</ul>  

Products模块的CSS样式表也更新一下,代码如下:

.products {
  margin: 0 0 2em 0;
  list-style-type: none;
  padding: 0;
  width: 15em;
}
.products li {
  position: relative;
  cursor: pointer;
  background-color: #EEE;
  margin: .5em;
  padding: .3em 0;
  height: 1.6em;
  border-radius: 4px;
}
 
.products li:hover {
  color: #607D8B;
  background-color: #DDD;
  left: .1em;
}
 
.products a {
  color: #888;
  text-decoration: none;
  position: relative;
  display: block;
  width: 250px;
}
 
.products a:hover {
  color:#607D8B;
}
 
.products .badge {
  display: inline-block;
  font-size: small;
  color: white;
  padding: 0.8em 0.7em 0 0.7em;
  background-color: #607D8B;
  line-height: 1em;
  position: relative;
  left: -1px;
  top: -4px;
  height: 1.8em;
  min-width: 16px;
  text-align: right;
  margin-right: .8em;
  border-radius: 4px 0 0 4px;
}
 
button {
  background-color: #eee;
  border: none;
  padding: 5px 10px;
  border-radius: 4px;
  cursor: pointer;
  cursor: hand;
  font-family: Arial;
}
 
button:hover {
  background-color: #cfd8dc;
}
 
button.delete {
  position: relative;
  left: 194px;
  top: -32px;
  background-color: gray !important;
  color: white;
}

对于product-detail模块做相应的修改,增加了删除和修改的功能。这些功能调用相应的product.service.ts中的相应方法。代码如下:

import { Component, OnInit, Input } from '@angular/core';  
import { Product } from '../product';  
import { ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';
import { ProductService }  from '../product.service';
@Component({  
  selector:'app-product-detail',  
  templateUrl:'./product-detail.component.html',  
  styleUrls:['./product-detail.component.css']  
})  
  
export class ProductDetailComponent implements OnInit {  
  product: Product;     
  constructor(private route: ActivatedRoute,
    private productService: ProductService,
    private location: Location) { }  
  
  ngOnInit() {  
    this.getProduct()
  }

  getProduct() {
    const id = +this.route.snapshot.paramMap.get('id');
    this.productService.getProduct(id)
      .subscribe(product => this.product = product);  
  }

  goBack(): void {
    this.location.back();
  }

  save(): void {
   this.productService.updateProduct(this.product)
     .subscribe(() => this.goBack());
  }

  delete(): void {
   this.productService.deleteProduct(this.product)
     .subscribe(() => this.goBack());
  }
}  

相应的product-detail.component.html也要修改一下,增加几个对应的按钮:

<div *ngIf="product">  
  <h2>{{ product.name |uppercase }} Details</h2>  
  <div><span>id:</span>{{product.id}}</div>  
  <div>  
    <label>name:  
      <input [(ngModel)]="product.name" placeholder="name"/>  
    </label>  
  </div>  
  <div>  
    <label>description:  
      <input [(ngModel)]="product.description" placeholder="description"/>  
    </label>  
  </div>  
  <button (click)="save()">save</button>
  <button (click)="delete()">delete</button>
  <button (click)="goBack()">go back</button>
</div> 

再新增一个add-product的模块,用于创建新产品。在my-app/目录下执行以下命令:

ng generate component add-product

修改新创建的add-product.component.ts文件,代码如下:

import { Component, OnInit } from '@angular/core';
import { Product } from '../product';  
import { ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';
import { ProductService }  from '../product.service';

@Component({
  selector: 'app-add-product',
  templateUrl: './add-product.component.html',
  styleUrls: ['./add-product.component.css']
})
export class AddProductComponent implements OnInit {
  constructor(private route: ActivatedRoute,
    private productService: ProductService,
    private location: Location) { }

  ngOnInit() {
  }

  create(name: String, description: string): void {
   this.productService.createProduct({name, description} as Product)
     .subscribe(() => this.goBack());
  }

  goBack(): void {
    this.location.back();
  }
}

对于add-product.component.html文件修改如下:

<div>  
  <h2>Create Product</h2>  
  <div>  
    <label>name:  
      <input #name />  
    </label>  
  </div>  
  <div>  
    <label>description:  
      <input #description />  
    </label>  
  </div>  
  <button (click)="create(name.value, description.value); name.value=''; description.value=''">create</button>
  <button (click)="goBack()">go back</button>
</div> 

修改完成后,在命令行下输入:

ng serve -open

最终完成的效果如下:


猜你喜欢

转载自blog.csdn.net/gzroy/article/details/80231476