博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【CuteJavaScript】Angular6入门项目(4.改造组件和添加HTTP服务)
阅读量:6036 次
发布时间:2019-06-20

本文共 11350 字,大约阅读时间需要 37 分钟。

本文目录

  • 一、
  • 二、
  • 三、

    • 1.
    • 2.
    • 3.
  • 四、

    • 1.
    • 2.
  • 五、

    • 1.
    • 2.
    • 3.
  • 六、改造组件

    • 1.添加历史记录组件
    • 2.添加和删除历史记录
  • 七、HTTP改造

    • 1.引入HTTP
    • 2.通过HTTP请求数据
    • 3.通过HTTP修改数据
    • 4.通过HTTP增加数据
    • 5.通过HTTP删除数据
    • 6.通过HTTP查找数据

本项目源码放在

六、改造组件

从这里开始,我们要使用RxJS来改造组件和添加新功能了,让整个项目更加完善。

1.添加历史记录组件

  • 创建HistoryComponent组件
ng g component hostory

然后在app.component.html文件夹中添加组件:

2.添加增删改查功能

这里我们要开始做书本的增删改查功能,需要先创建一个HistoryService服务,方便我们实现这几个功能:

  • 创建HistoryService服务
ng g service history

然后在生成的ts文件中,增加addclear方法,add方法用来添加历史记录到history数组中,clear方法则是清空history数组:

// history.service.tsexport class HistoryService {    history: string[] = [];    add(history: string){        this.history.push(history);    }    clear(){        this.history = [];    }}
  • 使用HistoryService服务

在将这个服务,注入到BooksService中,并改造getBooks方法:

// books.service.tsimport { HistoryService } from './history.service';constructor(    private historyservice: HistoryService) { }getBooks(): void{    this.historyservice.add('请求书本数据')    this.booksservice.getBookList()        .subscribe(books => this.books = books);}

也可以用相同方法,在IndexComponent中添加访问首页书本列表的记录。

// index.component.tsimport { HistoryService } from '../history.service';constructor(    private booksservice: BooksService,    private historyservice: HistoryService) { }getBooks(): void{    this.historyservice.add('访问首页书本列表');    this.booksservice.getBookList()        .subscribe(books => this.books = books);}

接下来,将我们的HistoryService注入到HistoryComponent中,然后才能将历史数据显示到页面上:

// history.component.tsimport { HistoryService } from '../history.service';export class HistoryComponent implements OnInit {    constructor(private historyservice: HistoryService) { }    ngOnInit() {}}

操作历史:

{
{item}}

代码解释

*ngIf="historyservice.history.length",是为了防止还没有拿到历史数据,导致后面的报错。
(click)="historyservice.clear()", 绑定我们服务中的clear事件,实现清除缓存。
*ngFor="let item of historyservice.history",将我们的历史数据渲染到页面上。

到了这一步,就能看到历史数据了,每次也换到首页,都会增加一条。

图片5-1

接下来,我们要在书本详情页也加上历史记录的统计,导入文件,注入服务,然后改造getBooks方法,实现历史记录的统计:

// detail.component.tsimport { HistoryService } from '../history.service';export class DetailComponent implements OnInit {    constructor(        private route: ActivatedRoute,        private location: Location,        private booksservice: BooksService,        private historyservice: HistoryService    ) { }    //...    getBooks(id: number): void {        this.books = this.booksservice.getBook(id);        this.historyservice.add(`查看书本${this.books.title},id为${this.books.id}`);        console.log(this.books)    }}

图片5-2

这时候就可以在历史记录中,看到这些操作的记录了,并且清除按钮也正常使用。

七、HTTP改造

原本我只想写到上一章,但是想到,我们实际开发中,哪有什么本地数据,基本上数据都是要从服务端去请求,所以这边也有必要引入这一张,模拟实际的HTTP请求。

1.引入HTTP

在这一章,我们使用Angular提供的 HttpClient 来添加一些数据持久化特性。

然后实现对书本数据进行获取,增加,修改,删除和查找功能。

HttpClient是Angular通过 HTTP 与远程服务器通讯的机制。

这里我们为了让HttpClient在整个应用全局使用,所以将HttpClient导入到根模块app.module.ts中,然后把它加入 @NgModule.imports 数组:

import { HttpClientModule } from '@angular/common/http';@NgModule({    //...    imports: [        BrowserModule,        AppRoutingModule,        HttpClientModule    ],    //...})

这边我们使用 模拟出的远程数据服务器通讯。

注意: 这个内存 Web API 模块与 Angular 中的 HTTP 模块无关。

通过下面命令来安装:

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

然后在app.module.ts中导入 HttpClientInMemoryWebApiModuleInMemoryDataService 类(后面创建):

// app.module.tsimport { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';import { InMemoryDataService }  from './in-memory-data.service';@NgModule({    // ...    imports: [        // ...        HttpClientInMemoryWebApiModule.forRoot(        InMemoryDataService, {dataEncapsulation:false}        )    ],    // ...})export class AppModule { }

知识点:

forRoot() 配置方法接受一个 InMemoryDataService 类(初期的内存数据库)作为参数。

然后我们要创建InMemoryDataService类:

ng g service InMemoryData

并将生成的in-memory-data.service.ts修改为:

// in-memory-data.service.tsimport { Injectable } from '@angular/core';import { InMemoryDbService } from 'angular-in-memory-web-api';import { Books } from './books';@Injectable({  providedIn: 'root'})export class InMemoryDataService implements InMemoryDbService {  createDb(){    const books = [      {          id: 1,           url: 'https://img3.doubanio.com/view/subject/m/public/s29988481.jpg',          title: '像火焰像灰烬',          author: '程姬',      },      // 省略其他9条数据    ];    return {books};  }  constructor() { }}

这里先总结InMemoryDbService所提供的RESTful API,后面都要用到:

例如如果urlapi/books,那么

  • 查询所有成员:以GET方法访问api/books
  • 查询某个成员:以GET方法访问api/books/id,比如id1,那么访问api/books/1
  • 更新某个成员:以PUT方法访问api/books/id
  • 删除某个成员:以DELETE方法访问api/books/id
  • 增加一个成员:以POST方法访问api/books

2.通过HTTP请求数据

现在要为接下来的网络请求做一些准备,先在books.service.ts中引入HTTP符号,然后注入HttpClient并改造:

// books.service.tsimport { HttpClient, HttpHeaders} from '@angular/common/http';// ...export class BooksService {    constructor(        private historyservice: HistoryService,        private http: HttpClient    ) { }    private log(histories: string){        this.historyservice.add(`正在执行:${histories}`)    }    private booksUrl = 'api/books'; // 提供一个API供调用    // ...}

这里我们还新增一个私有方法log和一个私有变量booksUrl

接下来我们要开始发起http请求数据,开始改造getBookList方法:

// books.service.ts// ...getBookList(): Observable
{ this.historyservice.add('请求书本数据') return this.http.get
(this.booksUrl);}// ...

这里我们使用 http.get 替换了 of,其它没修改,但是应用仍然在正常工作,这是因为这两个函数都返回了 Observable<Hero[]>

实际开发中,我们还需要考虑到请求的错误处理,要捕获错误,我们就要使用 RxJS 的 catchError() 操作符来建立对 Observable 结果的处理管道(pipe)。

我们引入catchError 并改造原本getBookList方法:

// books.service.tsgetBookList(): Observable
{ this.historyservice.add('请求书本数据') return this.http.get
(this.booksUrl).pipe( catchError(this.handleError
('getHeroes', [])) );}private handleError
(operation = 'operation', result?: T) { return (error: any): Observable
=> { this.log(`${operation} 失败: ${error.message}`); // 发出错误通知 return of(result as T); // 返回空结果避免程序出错 };}

知识点

.pipe() 方法用来扩展 Observable 的结果。
catchError() 操作符会拦截失败的 Observable。并把错误对象传给错误处理器,错误处理器会处理这个错误。
handleError() 错误处理函数做了两件事,发出错误通知和返回空结果避免程序出错。

这里还需要使用tap操作符改造getBookList方法,来窥探Observable数据流,它会查看Observable的值,然后我们使用log方法,记录一条历史记录。

tap 回调不会改变这些值本身。

// books.service.tsgetBookList(): Observable
{ return this.http.get
(this.booksUrl) .pipe( tap( _ => this.log('请求书本数据')), catchError(this.handleError
('getHeroes', [])) );}

3.通过HTTP修改数据

这里我们需要在原来DetailComponent上面,添加一个输入框、保存按钮和返回按钮,就像这样:

修改信息:

这边切记一点,一定要在app.module.ts中引入 FormsModule模块,并在@NgModuleimports中引入,不然要报错了。

// app.module.ts// ...import { FormsModule } from '@angular/forms'; @NgModule({    // ...    imports: [        // ...        FormsModule    ],    // ...})

input框绑定书本的标题books.title,而保存按钮绑定一个save()方法,这里还要实现这个方法:

// detail.component.tssave(): void {    this.historyservice.updateBooks(this.books)        .subscribe(() => this.goBack());}goBack(): void {    this.location.back();}

这里通过调用BooksServiceupdateBooks方法,将当前修改后的书本信息修改到源数据中,这里我们需要去books.service.ts中添加updateBooks方法:

// books.service.ts// ...updateBooks(books: Books): Observable
{ return this.http.put(this.booksUrl, books, httpOptions).pipe( tap(_ => this.log(`修改书本的id是${books.id}`)), catchError(this.handleError
(`getBooks请求是id为${books.id}`)) )}// ...

知识点

HttpClient.put() 方法接受三个参数:URL 地址要修改的数据其他选项
httpOptions 常量需要定义在@Injectable修饰器之前。

现在,我们点击首页,选择一本书进入详情,修改标题然后保存,会发现,首页上这本书的名称也会跟着改变呢。这算是好了。

4.通过HTTP增加数据

我们可以新增一个页面,并添加上路由和按钮:

ng g component add

添加路由:

// app-routing.module.ts// ...import { AddComponent } from './add/add.component';const routes: Routes = [  { path: '', redirectTo:'/index', pathMatch:'full' },  { path: 'index', component: IndexComponent},  { path: 'detail/:id', component: DetailComponent},  { path: 'add', component: AddComponent},]

添加路由入口:

添加书本

编辑添加书本的页面:

添加书本:

初始化添加书本的数据:

// add.component.ts// ...import { Books } from '../books';import { BooksService } from '../books.service';import { HistoryService } from '../history.service';import { Location } from '@angular/common';export class AddComponent implements OnInit {    books: Books = {        id: 0,        url: '',        title: '',        author: ''    }    constructor(        private location: Location,        private booksservice: BooksService,        private historyservice: HistoryService    ) { }    ngOnInit() {}    add(books: Books): void{        books.title = books.title.trim();        books.author = books.author.trim();        this.booksservice.addBooks(books)        .subscribe( book => {            this.historyservice.add(`新增书本${books.title},id为${books.id}`);            this.location.back();        });    }}

然后在books.service.ts中添加addBooks方法,来添加一本书本的数据:

// books.service.tsaddBooks(books: Books): Observable
{ return this.http.post
(this.booksUrl, books, httpOptions).pipe( tap((newBook: Books) => this.log(`新增书本的id为${newBook.id}`)), catchError(this.handleError
('添加新书')) );}

现在就可以正常添加书本啦。

图片5-3

5.通过HTTP删除数据

这里我们先为每个书本后面添加一个删除按钮,并绑定删除事件delete

X
// books.component.tsimport { BooksService } from '../books.service';export class BooksComponent implements OnInit {  @Input() list: Books;  constructor(    private booksservice: BooksService  ) { }  // ...  delete(books: Books): void {    this.booksservice.deleteBooks(books)      .subscribe();  }}

然后还要再books.service.ts中添加deleteBooks方法来删除:

// books.service.tsdeleteBooks(books: Books): Observable
{ const id = books.id; const url = `${this.booksUrl}/${id}`; return this.http.delete
(url, httpOptions).pipe( tap(_ => this.log(`删除书本${books.title},id为${books.id}`)), catchError(this.handleError
('删除书本')) );}

这里需要在删除书本结束后,通知IndexComponent将数据列表中的这条数据删除,这里还需要再了解一下。

然后我们在父组件IndexComponent上添加change事件监听,并传入本地的funChange

在对应的index.component.ts中添加funChange方法:

// index.component.tsfunChange(books, $event){    this.books = this.books.filter(h => h.id !== books.id);}

再来,我们在子组件BooksComponent上多导入OutputEventEmitter,并添加@Output()修饰器和调用emit

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';export class BooksComponent implements OnInit {    // ...     @Output()    change = new EventEmitter()    // ...     delete(books: Books): void {        this.booksservice.deleteBooks(books)        .subscribe(()=>{            this.change.emit(books);        });    }}

这样就实现了我们父子组件之间的事件传递啦,现在我们的页面还是正常运行,并且删除一条数据后,页面数据会更新。

6.通过HTTP查找数据

还是在books.service.ts,我们添加一个方法getBooks,来实现通过ID来查找指定书本,因为我们是通过ID查找,所以返回的是单个数据,这里就是Observable<Books>类型:

// books.service.tsgetBooks(id: number): Observable
{ const url = `${this.booksUrl}/${id}`; return this.http.get
(url).pipe( tap( _ => this.log(`请求书本的id为${id}`)), catchError(this.handleError
(`getBooks请求是id为${id}`)) )}

注意,这里 getBooks 会返回 Observable<Books>,是一个可观察的单个对象,而不是一个可观察的对象数组。

八、结语

这个项目其实很简单,但是我还是一步一步的写下来,一方面让自己更熟悉Angular,另一方面也是希望能帮助到更多朋友哈~

最终效果:

图片结果

本部分内容到这结束

Author 王平安
E-mail pingan8787@qq.com
博 客 www.pingan8787.com
微 信 pingan8787
每日文章推荐
JS小册 js.pingan8787.com
微信公众号 前端自习课

前端自习课

转载地址:http://uslhx.baihongyu.com/

你可能感兴趣的文章
Retrofit 入门学习
查看>>
jQuery对象与dom对象的转换
查看>>
Atitit.html css 浏览器原理理论概论导论attilax总结
查看>>
DotNet项目中的一些常用验证操作
查看>>
Android之父Andy Rubin:被乔布斯羡慕嫉妒的天才
查看>>
C#winform省市县联动,以及有的县是空值时显示异常的处理
查看>>
android intent 传递list或者对象
查看>>
修改activeMQ端口号
查看>>
django 简单的邮件系统
查看>>
深度学习-Caffe编译测试的小总结
查看>>
解决MYSQL错误:ERROR 1040 (08004): Too many connections
查看>>
【树莓派】树莓派网络配置:静态IP、无线网络、服务等
查看>>
JavaScript——双向链表实现
查看>>
git服务器新增仓库
查看>>
Appium+python自动化7-输入中文
查看>>
抽象类和借口的区别
查看>>
WebConfig配置文件详解
查看>>
nginx的location root 指令
查看>>
zDiaLog弹出层
查看>>
linux不常用但很有用的命令(持续完善)
查看>>