数据的增删改查

数据的增删改查,第1张

模拟数据服务器

这里可以参考:Hero guide 从服务器端获取数据_w3cschool

本应用将使用 内存 Web API(In-memory Web API) 模拟出的远程数据服务器通讯,并且给出仿真的响应。安装这个内存 Web API 包:

npm install angular-in-memory-web-api --save

在 AppModule 中需要导入:

import { HttpClientModule} from '@angular/common/http';
import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
import { InMemoryDataService }  from './in-memory-data.service';
imports: [
    HttpClientInMemoryWebApiModule.forRoot(
      InMemoryDataService, { dataEncapsulation: false }
    ),
    HttpClientModule,
    BrowserModule
  ]

forRoot() 配置方法接收一个 InMemoryDataService 类来初始化内存数据库。

创建一个in-memory-data.service:ng generate service InMemoryData

src/app/in-memory-data.service.ts

import { Injectable } from '@angular/core';
import { InMemoryDbService } from 'angular-in-memory-web-api';
import { Hero } from './hero';

@Injectable({
  providedIn: 'root'
})
export class InMemoryDataService implements InMemoryDbService {

  constructor() { }
  createDb() {
    const heroes = [
      { id: 11, name: 'Dr Nice' },
      { id: 12, name: 'Narco' },
      { id: 13, name: 'Bombasto' },
      { id: 14, name: 'Celeritas' },
      { id: 15, name: 'Magneta' },
      { id: 16, name: 'RubberMan' },
      { id: 17, name: 'Dynama' },
      { id: 18, name: 'Dr IQ' },
      { id: 19, name: 'Magma' },
      { id: 20, name: 'Tornado' }
    ];
    return {heroes};
  }
  genId(heroes: Hero[]): number {
    return heroes.length > 0 ? Math.max(...heroes.map(hero => hero.id)) + 1 : 11;
  }
}
genId(heroes: Hero[]): number {
    return heroes.length > 0 ? Math.max(...heroes.map(hero => hero.id)) + 1 : 11;
  }

重写genId方法以确保英雄总是有一个id,如果heroes数组为空,下面的方法将返回初始值(11)。如果heroes数组不是空的,下面的方法将返回最高的值。

创建一个hero.service用来处理数据的请求,模拟处理数据的接口:ng g service hero

src/app/hero.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';
import { Hero } from './hero';
@Injectable({ providedIn: 'root' })
export class HeroService {
  private heroesUrl = 'api/heroes'; 
  httpOptions = {
    headers: new HttpHeaders({ 'Content-Type': 'application/json' })
  };
  constructor(
    private http: HttpClient
   ) { }
  /** GET heroes from the server */
  getHeroes(): Observable {
    return this.http.get(this.heroesUrl)
      .pipe(
        tap(_ => this.log('fetched heroes')),
        catchError(this.handleError('getHeroes', []))
      );
  }
  /** GET hero by id. Will 404 if id not found */
  getHero(id: number): Observable {
    const url = `${this.heroesUrl}/${id}`;
    return this.http.get(url).pipe(
      tap(_ => this.log(`fetched hero id=${id}`)),
      catchError(this.handleError(`getHero id=${id}`))
    );
  }
  /* GET heroes whose name contains search term */
  searchHeroes(term: string): Observable {
    if (!term.trim()) {
      // if not search term, return empty hero array.
      return of([]);
    }
    return this.http.get(`${this.heroesUrl}/?name=${term}`).pipe(
      tap(x => x.length ?
         this.log(`found heroes matching "${term}"`) :
         this.log(`no heroes matching "${term}"`)),
      catchError(this.handleError('searchHeroes', []))
    );
  }
   Save methods //
  /** POST: add a new hero to the server */
  addHero(hero: Hero): Observable {
    return this.http.post(this.heroesUrl, hero, this.httpOptions).pipe(
      tap((newHero: Hero) => this.log(`added hero w/ id=${newHero.id}`)),
      catchError(this.handleError('addHero'))
    );
  }
  /** DELETE: delete the hero from the server */
  deleteHero(hero: Hero | number): Observable {
    const id = typeof hero === 'number' ? hero : hero.id;
    const url = `${this.heroesUrl}/${id}`;
    return this.http.delete(url, this.httpOptions).pipe(
      tap(_ => this.log(`deleted hero id=${id}`)),
      catchError(this.handleError('deleteHero'))
    );
  }
  /** PUT: update the hero on the server */
  updateHero(hero: Hero): Observable {
    return this.http.put(this.heroesUrl, hero, this.httpOptions).pipe(
      tap(_ => this.log(`updated hero id=${hero.id}`)),
      catchError(this.handleError('updateHero'))
    );
  }
  private handleError(operation = 'operation', result?: T) {
    return (error: any): Observable => {
      console.error(error); 
      this.log(`${operation} failed: ${error.message}`);
      return of(result as T);
    };
  }
  private log(message: string) {
    `HeroService: ${message}`
  }
}

private heroesUrl = 'api/heroes': 'api/heroes'是后端API的URL路径,因为这里用了In-memory Web API,所以"heroes"的部分需要和 "in-memory-data-service.ts" 中的数据对象对上,但是"api/"这里随便填啥都可以的,它类似于一个占位符,即使是“aaa/”也能取到数据,因为所有请求的URL都会被In-memory Web API截断,然后返回InMemoryDataService里的数据。

当前的 HeroService.getHeroes() 使用HttpClient 的 http.get() 函数来把模拟英雄数据返回为 Observable 格式。

 getHeroes(): Observable {
    return this.http.get(this.heroesUrl)
      .pipe(
        tap(_ => this.log('fetched heroes')),
        catchError(this.handleError('getHeroes', []))
      );
  }

英雄 Web API 在保存时的请求中有一个特殊的请求头。 这个请求头是在 HeroService 的 httpOptions 常量中定义的:

 httpOptions = {
    headers: new HttpHeaders({ 'Content-Type': 'application/json' })
  };

handleError:

 private handleError(operation = 'operation', result?: T) {
    return (error: any): Observable => {
      console.error(error); 
      this.log(`${operation} failed: ${error.message}`);
      return of(result as T);
    };
  }

它不再直接处理错误,而是返回给 catchError 返回一个错误处理函数,控制台中汇报了这个错误之后,这个处理器会汇报一个用户友好的消息,并给应用返回一个安全值,让应用继续工作。

窥探 Observable:

getHeroes(): Observable {
    return this.http.get(this.heroesUrl)
      .pipe(
        tap(_ => this.log('fetched heroes')),
        catchError(this.handleError('getHeroes', []))
      );
 }

HeroService 的方法将会窥探 Observable 的数据流,并通过 log() 方法往页面底部发送一条消息。

它们可以使用 RxJS 的 tap() *** 作符来实现,该 *** 作符会查看 Observable 中的值,并且把它们传出来。

通过 id 获取英雄:

 getHero(id: number): Observable {
    const url = `${this.heroesUrl}/${id}`;
    return this.http.get(url).pipe(
      tap(_ => this.log(`fetched hero id=${id}`)),
      catchError(this.handleError(`getHero id=${id}`))
    );
  }

这里的 baseURL 就是在 英雄列表与 HTTP 部分定义过的 heroesURL(api/heroes)。通过你要获取的英雄的编号id,getHero() 会返回 Observable(“一个可观察的单个英雄对象”)。

修改英雄:

updateHero(hero: Hero): Observable {
    return this.http.put(this.heroesUrl, hero, this.httpOptions).pipe(
      tap(_ => this.log(`updated hero id=${hero.id}`)),
      catchError(this.handleError('updateHero'))
    );
  }

它会使用 http.put() 来把修改后的英雄保存到服务器上。

HttpClient.put() 方法接受三个参数:

URL 地址

要修改的数据(这里就是修改后的英雄)

选项

添加新英雄:

 addHero(hero: Hero): Observable {
    return this.http.post(this.heroesUrl, hero, this.httpOptions).pipe(
      tap((newHero: Hero) => this.log(`added hero w/ id=${newHero.id}`)),
      catchError(this.handleError('addHero'))
    );
  }

它调用 HttpClient.post() ,它期待服务器为这个新的英雄生成一个 id,然后把它通过 Observable 返回给调用者,刷新浏览器,并添加一些英雄。

删除某个英雄:

deleteHero(hero: Hero | number): Observable {
    const id = typeof hero === 'number' ? hero : hero.id;
    const url = `${this.heroesUrl}/${id}`;
    return this.http.delete(url, this.httpOptions).pipe(
      tap(_ => this.log(`deleted hero id=${id}`)),
      catchError(this.handleError('deleteHero'))
    );
  }

deleteHero() 调用了 HttpClient.delete(),URL 就是英雄的资源 URL 加上要删除的英雄的 id,不用像 put() 和 post() 中那样发送任何数据。

根据名字搜索:

searchHeroes(term: string): Observable {
    if (!term.trim()) {
      // if not search term, return empty hero array.
      return of([]);
    }
    return this.http.get(`${this.heroesUrl}/?name=${term}`).pipe(
      tap(x => x.length ?
         this.log(`found heroes matching "${term}"`) :
         this.log(`no heroes matching "${term}"`)),
      catchError(this.handleError('searchHeroes', []))
    );
  }

如果没有搜索词,该方法立即返回一个空数组。 剩下的部分和 getHeroes() 很像。 唯一的不同点是 URL,它包含了一个由搜索词组成的查询字符串。

现在我们就拥有了 *** 作数据的模拟接口,相当于布置好了后端,接下来回到前端的编辑。


src/app/blue.component.html


  

blue works!

                                                       
idnameoperation
{{item.id}}{{item.name}}                      
               

src/app/blue.component.css

.blueBox{
  height: 100%;
  background-color: lightblue;
  padding: 10px;
}
.testTable{
  border-collapse: collapse;
  margin: 10px;
  border: 2px solid #ccc;
  background-color: #fff;
}
.testTable>tr:first-child>td{
  font-size: 16px;
  background-color: #eff3f8;
  border-bottom: 2px solid #cccccc;
}
.testTable>tr{
  border-bottom: 1px solid #cccccc;
}

.testTable>tr>td{
  text-align: center;
  padding: 8px 10px;
  font-size: 12px;
  color: lightslategrey;
}
.btn{
  padding: 3px 5px;
  margin-left:8px;
  background-color:#778899;
  border: none;
}
.btn>*{
  font-size: 14px;
  color: #fff;
}

src/app/blue.component.ts

import { AddModalComponent } from './../add-modal/add-modal.component';
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import {MatDialog} from '@angular/material/dialog';
import { Hero } from '../hero';
import { HeroService } from '../hero.service';
import { Subject } from 'rxjs';

export interface DialogData {
  animal: string;
  name: string;
}
@Component({
  selector: 'app-blue',
  templateUrl: './blue.component.html',
  styleUrls: ['./blue.component.css']
})
export class BlueComponent implements OnInit {
  putName:any = null;
  list: Hero[] =[];
  private searchTerms = new Subject();

  constructor(private router:Router,
    public dialog: MatDialog,
    private heroService: HeroService) {
  }
  ngOnInit(): void {
    this.getHeroes();
    console.log(this.list);
  }
  openDialog(): void {
    const dialogRef = this.dialog.open(AddModalComponent, {
      width: '350px',
    });
    dialogRef.afterClosed().subscribe(result => {
      console.log(`Dialog result: ${result}`);
      if(result === true){
        this.getHeroes()
      }
    });
  }
  del(hero: Hero):void{
    this.list = this.list.filter(h => h.id !== hero.id);
    this.heroService.deleteHero(hero).subscribe();
  }
  change(item:any){
    const dialogRef = this.dialog.open(AddModalComponent, {
      width: '350px',
      data: item
    });
    dialogRef.afterClosed().subscribe(result => {
      console.log(`Dialog result: ${result}`);
      if(result === true){
        this.getHeroes()
      }
    });
  }
  search(){
    if(this.putName){
      this.heroService.searchHeroes(this.putName)
      .subscribe(res=>{
        this.list = res
      })
    }else{
      this.getHeroes()
    }
  }
  getHeroes(): void {
    this.heroService.getHeroes()
    .subscribe(heroes => this.list = heroes);
  }
}
 getHeroes(): void {
    this.heroService.getHeroes()
    .subscribe(heroes => this.list = heroes);
  }

该方法可以heroService.getHeroes()获取到所有的英雄信息。

不要忘了导入 import { Hero } from '../hero'; import { HeroService } from '../hero.service';

删除:

del(hero: Hero):void{
    this.list = this.list.filter(h => h.id !== hero.id);
    this.heroService.deleteHero(hero).subscribe();
  }

虽然这个组件把删除英雄的逻辑委托给了 HeroService,但仍保留了更新它自己的英雄列表的职责。 组件的 del() 方法会在 HeroService 对服务器的 *** 作成功之前,先从列表中移除要删除的英雄。

查询:

search(){
    if(this.putName){
      this.heroService.searchHeroes(this.putName)
      .subscribe(res=>{
        this.list = res
      })
    }else{
      this.getHeroes()
    }
  }

如果输入框中有值就会通过heroService.searchHeroes(this.putName)反馈出相对应的值并赋值给list然后渲染到页面上。如果输入框中没有值则调用getHeroes()方法。

在对数据新增、编辑的 *** 作的过程中我运用到了Angular Material的模态框组件Angular Material 对话框详细:

ng g component addModal

在app.module.ts里导入import {MatDialogModule} from '@angular/material/dialog';

当点击增加按钮时触发:

 openDialog(): void {
    const dialogRef = this.dialog.open(AddModalComponent, {
      width: '350px',
    });
    dialogRef.afterClosed().subscribe(result => {
      console.log(`Dialog result: ${result}`);
      if(result === true){
        this.getHeroes()
      }
    });
  }

当点击编辑按钮时触发:

 change(item:any){
    const dialogRef = this.dialog.open(AddModalComponent, {
      width: '350px',
      data: item
    });
    dialogRef.afterClosed().subscribe(result => {
      console.log(`Dialog result: ${result}`);
      if(result === true){
        this.getHeroes()
      }
    });
  }

两者都是打开同一个模态框,不同的是点击编辑按钮会向模态框传一个item的值。

当点击按钮,就会open AddModalComponent 组件并给它定义一个width。

src/app/add-model.component.html

New data

  
    name
    
  


  
  

在需要处理Form格式数据时,要在app.module.ts中导入import{FormsModule}from '@angular/forms';并注册。

src/app/add-model.component.ts

import { HttpClient } from '@angular/common/http';
import { Component, Inject, OnInit } from '@angular/core';
import { MatDialog ,  MAT_DIALOG_DATA } from '@angular/material/dialog';
import { DialogData } from '../blue/blue.component';
import { Hero } from '../hero';
import { HeroService } from '../hero.service';
@Component({
  selector: 'app-add-modal',
  templateUrl: './add-modal.component.html',
  styleUrls: ['./add-modal.component.css']
})
export class AddModalComponent implements OnInit {
  heroes:Hero[] =[];
  info:any={
    id:"",
    name:""
  }
  list:any[]=[]
  constructor(
    private http : HttpClient,
    private heroService: HeroService,
    @Inject(MAT_DIALOG_DATA) public data: DialogData,
  ) {}
  ngOnInit(): void {
    if(this.data){
      this.info = JSON.parse(JSON.stringify(this.data))
    }
  }
  addNews():void{
    if(this.data){
      this.heroService.updateHero(this.info as Hero)
     .subscribe();

    }else{
      let name = this.info.name.trim();
      if (!name) { return; }
      console.log(name);
      this.heroService.addHero({ name } as Hero)
      .subscribe(hero => {
        console.log(hero);
      // this.heroes.push(hero);
    });
    }
  }
}

@Inject(MAT_DIALOG_DATA) public data: DialogData专门接受传递到模态框的数据并可以在当前页面直接使用。

if(this.data){
      this.info = JSON.parse(JSON.stringify(this.data))
    }

对传递过来的数据进行一个深拷贝。

addNews():void{
    if(this.data){
      this.heroService.updateHero(this.info as Hero)
     .subscribe();
    }else{
      let name = this.info.name.trim();
      if (!name) { return; }
      console.log(name);
      this.heroService.addHero({ name } as Hero)
      .subscribe(hero => {
        console.log(hero);
      // this.heroes.push(hero);
    });
    }
  }

如果有数据传过来(说明是触发了编辑功能),通过heroService.updateHero(this.info as Hero)把this.info传给服务的 updateHero() 方法对heroes进行修改;

如果没有数据传过来(说明是触发了新增功能),这个处理器会用这个名字创建一个类似于 Hero 的对象(只缺少 id 属性),并把它传给服务的 addHero() 方法。

当点击模态框的OK按钮时,模态框会关闭并返回result = true,此时会触发:

 dialogRef.afterClosed().subscribe(result => {
      console.log(`Dialog result: ${result}`);
      if(result === true){
        this.getHeroes()
      }
    });

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/944874.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-18
下一篇 2022-05-18

发表评论

登录后才能评论

评论列表(0条)

保存