Jakeuj's Notes master Help

ABP.IO 新手教學 No.10 開發教學 第 9 部分:作者:用戶界面 {id="ABP-IO-Tutorial-No-10-Part-9-Authors-User-Interface"}

主要是之前做的 Author 使用 Anuglar 做展示層

先快速帶過

Web 應用程序開發教程 - 第 9 部分:作者:用戶界面

關於本教程

在本系列教程中,您將構建一個名為Acme.BookStore. 此應用程序用於管理書籍及其作者的列表。它是使用以下技術開發的:

  • Entity Framework Core作為 ORM 提供者。

  • Angular作為 UI 框架。

本教程分為以下幾個部分;

下載源代碼

本教程根據您的UI數據庫首選項有多個版本。我們準備了幾個要下載的源代碼組合:

介紹

這部分解釋瞭如何為Author前面部分介紹的實體創建 CRUD 頁面。

作者管理頁面

運行以下命令行以創建一個新模塊, AuthorModule在 angular 應用程序的根文件夾中命名:

yarn ng generate module author --module app --routing --route authors

此命令應產生以下輸出:

> yarn ng generate module author --module app --routing --route authors yarn run v1.19.1 $ ng generate module author --module app --routing --route authors CREATE src/app/author/author-routing.module.ts (344 bytes) CREATE src/app/author/author.module.ts (349 bytes) CREATE src/app/author/author.component.html (21 bytes) CREATE src/app/author/author.component.spec.ts (628 bytes) CREATE src/app/author/author.component.ts (276 bytes) CREATE src/app/author/author.component.scss (0 bytes) UPDATE src/app/app-routing.module.ts (1396 bytes) Done in 2.22s.

作者模塊

打開/src/app/author/author.module.ts並替換如下內容:

import { NgModule } from '@angular/core'; import { SharedModule } from '../shared/shared.module'; import { AuthorRoutingModule } from './author-routing.module'; import { AuthorComponent } from './author.component'; import { NgbDatepickerModule } from '@ng-bootstrap/ng-bootstrap'; @NgModule({ declarations: [AuthorComponent], imports: [SharedModule, AuthorRoutingModule, NgbDatepickerModule], }) export class AuthorModule {}
  • 添加了SharedModule. SharedModule導出一些創建用戶界面所需的常用模塊。

  • SharedModule已經導出了CommonModule ,所以我們已經刪除了CommonModule.

  • 添加NgbDatepickerModule了稍後將在作者創建和編輯表單中使用的內容。

菜單定義

打開src/app/route.provider.ts文件並添加以下菜單定義:

{ path: '/authors', name: '::Menu:Authors', parentName: '::Menu:BookStore', layout: eLayoutType.application, requiredPolicy: 'BookStore.Authors', }

最終的configureRoutes函數聲明應該如下:

function configureRoutes(routes: RoutesService) { return () => { routes.add([ { path: '/', name: '::Menu:Home', iconClass: 'fas fa-home', order: 1, layout: eLayoutType.application, }, { path: '/book-store', name: '::Menu:BookStore', iconClass: 'fas fa-book', order: 2, layout: eLayoutType.application, }, { path: '/books', name: '::Menu:Books', parentName: '::Menu:BookStore', layout: eLayoutType.application, requiredPolicy: 'BookStore.Books', }, { path: '/authors', name: '::Menu:Authors', parentName: '::Menu:BookStore', layout: eLayoutType.application, requiredPolicy: 'BookStore.Authors', }, ]); }; }

服務代理生成

ABP CLI提供了generate-proxy為您的 HTTP API 生成客戶端代理的命令,以便從客戶端輕鬆使用您的 HTTP API。在運行generate-proxy命令之前,您的主機必須已啟動並正在運行。

angular文件夾中運行以下命令:

abp generate-proxy

此命令為作者服務和相關模型 (DTO) 類生成服務代理:

書店角度服務代理作者

作者組件

打開/src/app/author/author.component.ts文件並替換如下內容:

import { Component, OnInit } from '@angular/core'; import { ListService, PagedResultDto } from '@abp/ng.core'; import { AuthorService, AuthorDto } from '@proxy/authors'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; import { ConfirmationService, Confirmation } from '@abp/ng.theme.shared'; @Component({ selector: 'app-author', templateUrl: './author.component.html', styleUrls: ['./author.component.scss'], providers: [ListService, { provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }], }) export class AuthorComponent implements OnInit { author = { items: [], totalCount: 0 } as PagedResultDto<AuthorDto>; isModalOpen = false; form: FormGroup; selectedAuthor = {} as AuthorDto; constructor( public readonly list: ListService, private authorService: AuthorService, private fb: FormBuilder, private confirmation: ConfirmationService ) {} ngOnInit(): void { const authorStreamCreator = (query) => this.authorService.getList(query); this.list.hookToQuery(authorStreamCreator).subscribe((response) => { this.author = response; }); } createAuthor() { this.selectedAuthor = {} as AuthorDto; this.buildForm(); this.isModalOpen = true; } editAuthor(id: string) { this.authorService.get(id).subscribe((author) => { this.selectedAuthor = author; this.buildForm(); this.isModalOpen = true; }); } buildForm() { this.form = this.fb.group({ name: [this.selectedAuthor.name || '', Validators.required], birthDate: [ this.selectedAuthor.birthDate ? new Date(this.selectedAuthor.birthDate) : null, Validators.required, ], }); } save() { if (this.form.invalid) { return; } if (this.selectedAuthor.id) { this.authorService .update(this.selectedAuthor.id, this.form.value) .subscribe(() => { this.isModalOpen = false; this.form.reset(); this.list.get(); }); } else { this.authorService.create(this.form.value).subscribe(() => { this.isModalOpen = false; this.form.reset(); this.list.get(); }); } } delete(id: string) { this.confirmation.warn('::AreYouSureToDelete', '::AreYouSure') .subscribe((status) => { if (status === Confirmation.Status.confirm) { this.authorService.delete(id).subscribe(() => this.list.get()); } }); } }

打開/src/app/author/author.component.html並替換如下內容:

<div class="card"> <div class="card-header"> <div class="row"> <div class="col col-md-6"> <h5 class="card-title"> {{ '::Menu:Authors' | abpLocalization }} </h5> </div> <div class="text-right col col-md-6"> <div class="text-lg-right pt-2"> <button id="create" class="btn btn-primary" type="button" (click)="createAuthor()"> <i class="fa fa-plus mr-1"></i> <span>{{ '::NewAuthor' | abpLocalization }}</span> </button> </div> </div> </div> </div> <div class="card-body"> <ngx-datatable [rows]="author.items" [count]="author.totalCount" [list]="list" default> <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)="editAuthor(row.id)"> {{ '::Edit' | abpLocalization }} </button> <button ngbDropdownItem (click)="delete(row.id)"> {{ '::Delete' | abpLocalization }} </button> </div> </div> </ng-template> </ngx-datatable-column> <ngx-datatable-column [name]="'::Name' | abpLocalization" prop="name"></ngx-datatable-column> <ngx-datatable-column [name]="'::BirthDate' | abpLocalization"> <ng-template let-row="row" ngx-datatable-cell-template> {{ row.birthDate | date }} </ng-template> </ngx-datatable-column> </ngx-datatable> </div> </div> <abp-modal [(visible)]="isModalOpen"> <ng-template #abpHeader> <h3>{{ (selectedAuthor.id ? '::Edit' : '::NewAuthor') | abpLocalization }}</h3> </ng-template> <ng-template #abpBody> <form [formGroup]="form" (ngSubmit)="save()"> <div class="form-group"> <label for="author-name">Name</label><span> * </span> <input type="text" id="author-name" class="form-control" formControlName="name" autofocus /> </div> <div class="form-group"> <label>Birth date</label><span> * </span> <input #datepicker="ngbDatepicker" class="form-control" name="datepicker" formControlName="birthDate" 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>

本地化

這個頁面使用了一些我們需要聲明的本地化鍵。打開項目文件夾en.json下的Localization/BookStore文件Acme.BookStore.Domain.Shared並添加以下條目:

"Menu:Authors": "Authors", "Authors": "Authors", "AuthorDeletionConfirmationMessage": "Are you sure to delete the author '{0}'?", "BirthDate": "Birth date", "NewAuthor": "New author"

運行應用程序

運行並登錄到應用程序。 由於您還沒有權限,因此您無法看到菜單項。 轉到identity/roles頁面,單擊操作按鈕並為管理員角色選擇權限操作:

書店作者權限

如您所見,admin 角色還沒有作者管理權限。單擊複選框並保存模式以授予必要的權限。 刷新頁面後,您將在主菜單中的Book Store下看到Authors菜單項:

書店作者頁面

就這樣!這是一個完整的 CRUD 頁面,您可以創建、編輯和刪除作者。

下一部分

請參閱本教程的下一部分

Jakeuj

PS5

  • ABP

  • 回首頁

本文章從點部落遷移至 Writerside

14 October 2025