资讯专栏INFORMATION COLUMN

【CuteJavaScript】Angular6入门项目(4.改造组件和添加HTTP服务)

BDEEFE / 859人阅读

摘要:然后我们在父组件上添加事件监听,并传入本地的在对应的中添加方法再来,我们在子组件上多导入和,并添加修饰器和调用这样就实现了我们父子组件之间的事件传递啦,现在我们的页面还是正常运行,并且删除一条数据后,页面数据会更新。

本文目录

一、项目起步

二、编写路由组件

三、编写页面组件

1.编写单一组件

2.模拟数据

3.编写主从组件

四、编写服务

1.为什么需要服务

2.编写服务

五、引入RxJS

1.关于RxJS

2.引入RxJS

3.改造数据获取方式

六、改造组件

1.添加历史记录组件

2.添加和删除历史记录

七、HTTP改造

1.引入HTTP

2.通过HTTP请求数据

3.通过HTTP修改数据

4.通过HTTP增加数据

5.通过HTTP删除数据

6.通过HTTP查找数据

本项目源码放在github

六、改造组件

从这里开始,我们要使用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.ts
export class HistoryService {
    history: string[] = [];
    add(history: string){
        this.history.push(history);
    }
    clear(){
        this.history = [];
    }
}

使用HistoryService服务

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

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

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

// index.component.ts
import { 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.ts
import { 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",将我们的历史数据渲染到页面上。

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

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

// detail.component.ts
import { 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)
    }
}

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

七、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(In-memory Web API) 模拟出的远程数据服务器通讯。
注意: 这个内存 Web API 模块与 Angular 中的 HTTP 模块无关。

通过下面命令来安装:

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

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

// app.module.ts
import { 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.ts
import { 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.ts
import { 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

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

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

// books.service.ts
getBookList(): 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.ts
getBookList(): 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.ts
save(): 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.ts
addBooks(books: Books): Observable{
    return this.http.post(this.booksUrl, books, httpOptions).pipe(
        tap((newBook: Books) => this.log(`新增书本的id为${newBook.id}`)),
        catchError(this.handleError("添加新书"))
    );
}

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

5.通过HTTP删除数据

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



X
// books.component.ts
import { 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.ts
deleteBooks(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将数据列表中的这条数据删除,这里还需要再了解一下Angular 父子组件数据通信。
然后我们在父组件IndexComponent上添加change事件监听,并传入本地的funChange


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

// index.component.ts
funChange(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.service.ts
getBooks(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,是一个可观察的单个对象,而不是一个可观察的对象数组。

八、结语

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

本部分内容到这结束

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102024.html

相关文章

  • CuteJavaScriptAngular6入门项目(2.构建项目页面组件

    摘要:编写单一组件我们首先写一个书本信息的组件,代码如下单个课本像火焰像灰烬程姬知识点是一个的复写器指令,就像中的和中的。写到这里,看看我们项目,还是一样正常在运行,只是现在项目中组件分工更加明确了。 本文目录 一、项目起步 二、编写路由组件 三、编写页面组件 1.编写单一组件 2.模拟数据 3.编写主从组件 四、编写服务 1.为什么需要服务 2.编写服务 五、...

    Lemon_95 评论0 收藏0
  • CuteJavaScriptAngular6入门项目(3.编写服务引入RxJS)

    摘要:发布通过回调方法向发布事件。观察者一个回调函数的集合,它知道如何去监听由提供的值。 本文目录 一、项目起步 二、编写路由组件 三、编写页面组件 1.编写单一组件 2.模拟数据 3.编写主从组件 四、编写服务 1.为什么需要服务 2.编写服务 五、引入RxJS 1.关于RxJS 2.引入RxJS 3.改造数据获取方式 六、改造组件 1.添...

    RebeccaZhong 评论0 收藏0
  • CuteJavaScriptAngular6入门项目(1.构建项目创建路由)

    摘要:启动服务,并打开新窗口可简写创建新组件可简写创建新服务创建路由模块其他另外还有很多的命令提供,详细可以查阅官方文档命令。引入路由模块导出路由模块的指令这里需要添加一个数组,并传入,导出让路由器的相关指令可以在中的组件中使用。 本文目录 一、项目起步 二、编写路由组件 三、编写页面组件 1.编写单一组件 2.模拟数据 3.编写主从组件 四、编写服务 1.为什么需要服务 2....

    bergwhite 评论0 收藏0
  • CuteJavaScript】GraphQL真香入门教程

    摘要:最终代码省略其他输入类型用标识查询类型需要至少定义一个不要会不显示查询这里需要转成数组因为前面定义了返回值是类型相当于数据库的添加操作相当于数据库的更新操作省略其他现在我们可以启动服务器,在上测试下效果了。 showImg(https://segmentfault.com/img/remote/1460000019142304?w=893&h=438); 看完复联四,我整理了这份 Gr...

    bingo 评论0 收藏0
  • angular6入门之安装angular-cli

    摘要:由于之前安装的是的版本,需要卸载了,安装最新的版本。清除缓存,确保卸载干净具体参考安装最新版本安装成功后提示意思是版的需要版本最低,我之前的是的版本。先安装,去下载安装。在命令行输入检查是否安装成功。 最近听了大漠老师分享的angular6的讲解(附个链接大漠老师课程:http://www.ngfans.net/topic/2...),像是沙漠中发现了绿洲一样,决定好好学习一番,于是准...

    loonggg 评论0 收藏0

发表评论

0条评论

BDEEFE

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<