3.通过HTTP修改数据
这里我们需要在原来DetailComponent
上面,添加一个输入框、保存按钮和返回按钮,就像这样:
<!-- detail.component.html --> <!-- 前面代码省略 --> <div> <h2>修改信息:</h2> <label>新标题: <input [(ngModel)]="books.title" placeholder="请输入新标题"> </label> <button (click)="save()">保存</button> <button (click)="goBack()">返回</button> </div>
这边切记一点,一定要在app.module.ts
中引入 FormsModule
模块,并在@NgModule
的imports
中引入,不然要报错了。
// 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(); }
这里通过调用BooksService
的updateBooks
方法,将当前修改后的书本信息修改到源数据中,这里我们需要去books.service.ts
中添加updateBooks
方法:
// books.service.ts // ... updateBooks(books: Books): Observable<any>{ return this.http.put(this.booksUrl, books, httpOptions).pipe( tap(_ => this.log(`修改书本的id是${books.id}`)), catchError(this.handleError<Books>(`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}, ]
添加路由入口:
<!-- app.component.html --> <!-- 省略一些代码 --> <a routerLink="/add">添加书本</a>
编辑添加书本的页面:
<!-- add.component.html --> <div class="add"> <h2>添加书本:</h2> <label>标题: <input [(ngModel)]="books.title" placeholder="请输入标题"> </label> <label>作者: <input [(ngModel)]="books.author" placeholder="请输入作者"> </label> <label>书本id: <input [(ngModel)]="books.id" placeholder="请输入书本id"> </label> <label>封面地址: <input [(ngModel)]="books.url" placeholder="请输入封面地址"> </label> <div><button (click)="add(books)">添加</button></div> </div>
初始化添加书本的数据:
// 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<Books>{ return this.http.post<Books>(this.booksUrl, books, httpOptions).pipe( tap((newBook: Books) => this.log(`新增书本的id为${newBook.id}`)), catchError(this.handleError<Books>('添加新书')) ); }
现在就可以正常添加书本啦。
5.通过HTTP删除数据
这里我们先为每个书本后面添加一个删除按钮,并绑定删除事件delete
:
<!-- books.component.html --> <!-- 省略一些代码 --> <span class="delete" (click)="delete(list)">X</span>
// 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<Books>{ const id = books.id; const url = `${this.booksUrl}/${id}`; return this.http.delete<Books>(url, httpOptions).pipe( tap(_ => this.log(`删除书本${books.title},id为${books.id}`)), catchError(this.handleError<Books>('删除书本')) ); }
这里需要在删除书本结束后,通知IndexComponent
将数据列表中的这条数据删除,这里还需要再了解一下Angular 父子组件数据通信。
然后我们在父组件IndexComponent
上添加change
事件监听,并传入本地的funChange
:
<!-- index.component.html --> <app-books *ngFor="let item of books" [list]="item" (change) = "funChange(item, $event)" ></app-books>
在对应的index.component.ts
中添加funChange
方法:
// index.component.ts funChange(books, $event){ this.books = this.books.filter(h => h.id !== books.id); }
再来,我们在子组件BooksComponent
上多导入Output
和EventEmitter
,并添加@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.ts getBooks(id: number): Observable<Books>{ const url = `${this.booksUrl}/${id}`; return this.http.get<Books>(url).pipe( tap( _ => this.log(`请求书本的id为${id}`)), catchError(this.handleError<Books>(`getBooks请求是id为${id}`)) ) }
注意,这里 getBooks
会返回 Observable<Books>
,是一个可观察的单个对象,而不是一个可观察的对象数组。
八、结语
这个项目其实很简单,但是我还是一步一步的写下来,一方面让自己更熟悉Angular,另一方面也是希望能帮助到更多朋友哈~ 最终效果: