在系列教程中,我们会构建一个基于ABP的Web应用程序,用于管理书籍及其作者列表
使用到的技术:
Entity Framework Core
Angular
本教程分为以下部分:
创建新书籍 本节我们完成一个基本的增删改操作
BookComponent 在/src/app/book/book.component.ts
中新增以下内容:
isModalOpen属性:用于判断模态框的打开关闭
createBook方法:新增按钮调用方法,打开弹窗
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import { ListService, PagedResultDto } from '@abp/ng.core'; import { Component, OnInit } from '@angular/core'; import { BookService, BookDto} from '@proxy/books'; @Component({ selector: 'app-book', templateUrl: './book.component.html', styleUrls: ['./book.component.scss'], providers: [ListService], }) export class BookComponent implements OnInit { book = { items: [], totalCount: 0 } as PagedResultDto<BookDto>; // 是否打开弹窗 isModalOpen = false; constructor( public readonly list: ListService, private bookService: BookService, ) {} ngOnInit() { const bookStreamCreator = (query) => this.bookService.getList(query); this.list.hookToQuery(bookStreamCreator).subscribe((response) => { this.book = response; }); } /** * 创建图书 */ createBook() { this.isModalOpen = true; } }
打开 /src/app/book/book.component.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <div class ="card" > <div class ="card-header" > <div class ="row" > <div class ="col col-md-6" > <h5 class ="card-title" > {{ '::Menu:Books' | abpLocalization }}</h5 > </div > <div class ="text-end col col-md-6" > <div class ="text-lg-end pt-2" > <button id ="create" class ="btn btn-primary" type ="button" (click )="createBook()" > <i class ="fa fa-plus mr-1" > </i > <span > {{ "::NewBook" | abpLocalization }}</span > </button > </div > </div > </div > </div > <div class ="card-body" > </div > </div > <abp-modal [(visible )]="isModalOpen" > <ng-template #abpHeader > <h3 > {{ '::NewBook' | abpLocalization }}</h3 > </ng-template > <ng-template #abpBody > </ng-template > <ng-template #abpFooter > <button type ="button" class ="btn btn-secondary" abpClose > {{ '::Close' | abpLocalization }} </button > </ng-template > </abp-modal >
这个时候,点击新增按钮弹窗如下:
添加弹窗表单 打开 /src/app/book/book.component.ts
新增以下内容:
从@angular/forms
导入 FormGroup
、 FormBuilder
、Validators
添加 form: FormGroup
变量
添加 bookTypes
属性作为 BookType
枚举成员列表,在表单图书类型选项中使用
注入FormBuilder
到构造函数
添加 buildForm
方法到文件末尾,在 createBook
方法调用 buildForm()
方法初始化
添加save
方法,用于保存表单填写数据到后台(发起请求)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 import { ListService, PagedResultDto } from '@abp/ng.core'; import { Component, OnInit } from '@angular/core'; import { BookService, BookDto, bookTypeOptions } from '@proxy/books'; // 导入 bookTypeOptions import { FormGroup, FormBuilder, Validators } from '@angular/forms'; // 导入 @Component({ selector: 'app-book', templateUrl: './book.component.html', styleUrls: ['./book.component.scss'], providers: [ListService], }) export class BookComponent implements OnInit { book = { items: [], totalCount: 0 } as PagedResultDto<BookDto>; form: FormGroup; // 定义form // 图书类型选项 bookTypes = bookTypeOptions; isModalOpen = false; constructor( public readonly list: ListService, private bookService: BookService, private fb: FormBuilder // 注入 FormBuilder ) {} ngOnInit() { const bookStreamCreator = (query) => this.bookService.getList(query); this.list.hookToQuery(bookStreamCreator).subscribe((response) => { this.book = response; }); } createBook() { this.buildForm(); // 打开模态框 初始化表单 this.isModalOpen = true; } // 初始化表单 buildForm() { this.form = this.fb.group({ name: ['', Validators.required], type: [null, Validators.required], publishDate: [null, Validators.required], price: [null, Validators.required], }); } // 新增保存方法 save() { if (this.form.invalid) { return; } this.bookService.create(this.form.value).subscribe(() => { this.isModalOpen = false; this.form.reset(); this.list.get(); }); } }
打开 /src/app/book/book.component.html
新增以下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 <abp-modal [(visible )]="isModalOpen" > <ng-template #abpHeader > <h3 > {{ (selectedBook.id ? '::Edit' : '::NewBook' ) | abpLocalization }}</h3 > </ng-template > <ng-template #abpBody > <form [formGroup ]="form" (ngSubmit )="save()" > <div class ="form-group" > <label for ="book-name" > Name</label > <span > * </span > <input type ="text" id ="book-name" class ="form-control" formControlName ="name" autofocus /> </div > <div class ="form-group" > <label for ="book-price" > Price</label > <span > * </span > <input type ="number" id ="book-price" class ="form-control" formControlName ="price" /> </div > <div class ="form-group" > <label for ="book-type" > Type</label > <span > * </span > <select class ="form-control" id ="book-type" formControlName ="type" > <option [ngValue ]="null" > Select a book type</option > <option [ngValue ]="type.value" *ngFor ="let type of bookTypes" > {{ type.key }}</option > </select > </div > <div class ="form-group" > <label > Publish date</label > <span > * </span > <input #datepicker ="ngbDatepicker" class ="form-control" name ="datepicker" formControlName ="publishDate" ngbDatepicker (click )="datepicker.toggle()" /> </div > </form > </ng-template > <ng-template #abpFooter > <button type ="button" class ="btn btn-secondary" abpClose > {{ '::Close' | abpLocalization }} </button > <button class ="btn btn-primary" (click )="save()" [disabled ]="form.invalid" > <i class ="fa fa-check mr-1" > </i > {{ '::Save' | abpLocalization }} </button > </ng-template > </abp-modal >
Datepicker 在这个组件中使用了NgBootstrap datepicker
,需要添加与此组件相关的依赖项,打开 /src/app/book/book.module.ts
新增以下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import { NgModule } from '@angular/core'; import { SharedModule } from '../shared/shared.module'; import { BookRoutingModule } from './book-routing.module'; import { BookComponent } from './book.component'; import { NgbDatepickerModule } from '@ng-bootstrap/ng-bootstrap'; // 导入 @NgModule({ declarations: [BookComponent], imports: [ BookRoutingModule, SharedModule, NgbDatepickerModule, // 注册 ] }) export class BookModule { }
在 /src/app/book/book.component.ts
中使用NgbDateAdapter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import { ListService, PagedResultDto } from '@abp/ng.core'; import { Component, OnInit } from '@angular/core'; import { BookService, BookDto, bookTypeOptions } from '@proxy/books'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; // 导入 import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; @Component({ selector: 'app-book', templateUrl: './book.component.html', styleUrls: ['./book.component.scss'], providers: [ ListService, { provide: NgbDateAdapter, useClass: NgbDateNativeAdapter } // 提供器 ], })
打开弹窗
更新书籍 打开 /src/app/book/book.component.ts
新增以下内容:
声明类型为 BookDto
的 selectedBook
变量,用于存储获取到的Book对象
添加了editBook
方法,根据选择的书籍 Id
设置 selectedBook
对象
替换 buildForm
方法使用 selectedBook
数据初始化表单
替换 createBook
方法,设置 selectedBook
为空
修改 save
方法,同时处理新增和更新操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 import { ListService, PagedResultDto } from '@abp/ng.core'; import { Component, OnInit } from '@angular/core'; import { BookService, BookDto, bookTypeOptions } from '@proxy/books'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; @Component({ selector: 'app-book', templateUrl: './book.component.html', styleUrls: ['./book.component.scss'], providers: [ListService, { provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }], }) export class BookComponent implements OnInit { book = { items: [], totalCount: 0 } as PagedResultDto<BookDto>; selectedBook = {} as BookDto; // 定义 selectedBook form: FormGroup; bookTypes = bookTypeOptions; isModalOpen = false; constructor( public readonly list: ListService, private bookService: BookService, private fb: FormBuilder ) {} ngOnInit() { const bookStreamCreator = (query) => this.bookService.getList(query); this.list.hookToQuery(bookStreamCreator).subscribe((response) => { this.book = response; }); } createBook() { this.selectedBook = {} as BookDto; // 创建时将selectedBook置空 this.buildForm(); this.isModalOpen = true; } // 编辑书籍根据选择的Id查询Book信息 editBook(id: string) { this.bookService.get(id).subscribe((book) => { this.selectedBook = book; this.buildForm(); this.isModalOpen = true; }); } buildForm() { // 判断selectedBook是否有值,区分是否编辑或新增 this.form = this.fb.group({ name: [this.selectedBook.name || '', Validators.required], type: [this.selectedBook.type || null, Validators.required], publishDate: [ this.selectedBook.publishDate ? new Date(this.selectedBook.publishDate) : null, Validators.required, ], price: [this.selectedBook.price || null, Validators.required], }); } // 保存 save() { if (this.form.invalid) { return; } // 判断selectedBook.id是否有值 // true:更新 // false:新增 const request = this.selectedBook.id ? this.bookService.update(this.selectedBook.id, this.form.value) : this.bookService.create(this.form.value); request.subscribe(() => { this.isModalOpen = false; this.form.reset(); this.list.get(); }); } }
添加表格操作按钮 打开 /src/app/book/book.component.html
在 ngx-datatable
第一列添加 ngx-datatable-column
定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <ngx-datatable-column [name ]="'::Actions' | abpLocalization" [maxWidth ]="150" [sortable ]="false" > <ng-template let-row ="row" ngx-datatable-cell-template > <div ngbDropdown container ="body" class ="d-inline-block" > <button class ="btn btn-primary btn-sm dropdown-toggle" data-toggle ="dropdown" aria-haspopup ="true" ngbDropdownToggle > <i class ="fa fa-cog mr-1" > </i > {{ '::Actions' | abpLocalization }} </button > <div ngbDropdownMenu > <button ngbDropdownItem (click )="editBook(row.id)" > {{ '::Edit' | abpLocalization }} </button > </div > </div > </ng-template > </ngx-datatable-column >
在表格的第一列添加了一个 Actions
下拉菜单,如下图所示:
同时如下所示更改 ng-template #abpHeader
部分:
1 2 3 <ng-template #abpHeader > <h3 > {{ (selectedBook.id ? '::Edit' : '::NewBook' ) | abpLocalization }}</h3 > </ng-template >
删除书籍 打开 /src/app/book/book.component.ts
注入 ConfirmationService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // ... // 新增导入 import { ConfirmationService, Confirmation } from '@abp/ng.theme.shared'; constructor( public readonly list: ListService, private bookService: BookService, private fb: FormBuilder, private confirmation: ConfirmationService // 注入 ) {} // 新增删除方法 delete(id: string) { this.confirmation.warn('::AreYouSureToDelete', '::AreYouSure').subscribe((status) => { if (status === Confirmation.Status.confirm) { this.bookService.delete(id).subscribe(() => this.list.get()); } }); }
添加删除按钮 打开 /src/app/book/book.component.html
修改 ngbDropdownMenu
添加删除按钮
1 2 3 4 5 6 <div ngbDropdownMenu > <button ngbDropdownItem (click )="delete(row.id)" > {{ '::Delete' | abpLocalization }} </button > </div >
点击 delete
操作调用 delete
方法,然后显示一个确认弹层如下图所示:
至此就完成了对Book的增删改操作
下一节 教程下一节