这里可以参考: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!
id
name
operation
{{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()
}
});
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)