项目

本文档有多个版本。请选择最适合您的选项。

UI
Database

Web 应用程序开发教程 - 第 2 部分:书籍列表页面

本地化

在开始 UI 开发之前,我们首先需要准备本地化文本(通常是在开发应用程序时按需进行)。

本地化文本位于 Acme.BookStore.Domain.Shared 项目的 Localization/BookStore 文件夹下:

bookstore-localization-files

打开 en.json 文件(英文翻译),并按以下内容修改:

{
  "Culture": "en",
  "Texts": {
    "Menu:Home": "首页",
    "Welcome": "欢迎",
    "LongWelcomeMessage": "欢迎使用本应用程序。这是一个基于 ABP 的启动项目。更多信息请访问 abp.io。",
    "Menu:BookStore": "书店",
    "Menu:Books": "书籍",
    "Actions": "操作",
    "Close": "关闭",
    "Delete": "删除",
    "Edit": "编辑",
    "PublishDate": "出版日期",
    "NewBook": "新书",
    "Name": "名称",
    "Type": "类型",
    "Price": "价格",
    "CreationTime": "创建时间",
    "AreYouSure": "确定吗?",
    "AreYouSureToDelete": "确定要删除此项吗?",
    "Enum:BookType.0": "未定义",
    "Enum:BookType.1": "冒险",
    "Enum:BookType.2": "传记",
    "Enum:BookType.3": "反乌托邦",
    "Enum:BookType.4": "奇幻",
    "Enum:BookType.5": "恐怖",
    "Enum:BookType.6": "科学",
    "Enum:BookType.7": "科幻",
    "Enum:BookType.8": "诗歌"
  }
}
  • 本地化键名是任意的,你可以设置任何名称。我们针对特定文本类型推荐一些约定:
    • 为菜单项添加 Menu: 前缀。
    • 使用 Enum:<枚举类型>.<枚举值><枚举类型>.<枚举值> 命名约定来本地化枚举成员。这样处理后,ABP 可以在某些适当场景下自动对枚举进行本地化。

如果本地化文件中未定义某个文本,它将回退到本地化键(遵循 ASP.NET Core 的标准行为)。

ABP 的本地化系统基于 ASP.NET Core 的标准本地化系统构建,并在许多方面进行了扩展。详情请参阅本地化文档

安装 NPM 包

注意:本教程基于 ABP v3.1.0+ 版本。如果你的项目版本较旧,请升级你的解决方案。如果你要升级现有的 v2.x 项目,请查看迁移指南

如果之前未操作,请打开一个新的命令行界面(终端窗口),进入你的 angular 文件夹,然后运行 yarn 命令以安装 NPM 包:

yarn

创建书籍页面

是时候创建一些可见且可用的内容了!在开发 Angular 前端应用程序时,我们将使用以下工具:

运行以下命令行,在 Angular 应用程序的根文件夹中创建一个名为 BookModule 的新模块:

yarn ng generate module book --module app --routing --route books

此命令应产生以下输出:

> yarn ng generate module book --module app --routing --route books

yarn run v1.19.1
$ ng generate module book --module app --routing --route books
CREATE src/app/book/book-routing.module.ts (336 bytes)
CREATE src/app/book/book.module.ts (335 bytes)
CREATE src/app/book/book.component.html (19 bytes)
CREATE src/app/book/book.component.spec.ts (614 bytes)
CREATE src/app/book/book.component.ts (268 bytes)
CREATE src/app/book/book.component.scss (0 bytes)
UPDATE src/app/app-routing.module.ts (1289 bytes)
Done in 3.88s.

BookModule

打开 /src/app/book/book.module.ts 并将内容替换为以下内容:

import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { BookRoutingModule } from './book-routing.module';
import { BookComponent } from './book.component';

@NgModule({
  declarations: [BookComponent],
  imports: [
    BookRoutingModule,
    SharedModule
  ]
})
export class BookModule { }

  • 添加了 SharedModuleSharedModule 导出了一些创建用户界面所需的通用模块。
  • SharedModule 已经导出了 CommonModule,因此我们移除了 CommonModule

路由

生成的代码将新的路由定义放在了 src/app/app-routing.module.ts 文件中,如下所示:

const routes: Routes = [
  // 其他路由定义...
  { path: 'books', loadChildren: () => import('./book/book.module').then(m => m.BookModule) },
];

现在,打开 src/app/route.provider.ts 文件,并按以下内容替换 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,
      },
    ]);
  };
}

RoutesService 是 ABP 提供的一项服务,用于配置主菜单和路由。

  • path 是路由的 URL。
  • name 是本地化的菜单项名称(详情请参阅本地化文档)。
  • iconClass 是菜单项的图标(默认可以使用 Font Awesome 图标)。
  • order 是菜单项的排序。
  • layout 是 BooksModule 路由的布局(预定义了三种布局类型:eLayoutType.applicationeLayoutType.accounteLayoutType.empty)。

更多信息,请查看 RoutesService 文档

服务代理生成

ABP CLI 提供了一个 generate-proxy 命令,可为你的 HTTP API 生成客户端代理,使客户端更容易使用你的 HTTP API。在运行 generate-proxy 命令之前,你的主机必须处于运行状态。

警告:IIS Express 存在一个问题:它不允许从另一个进程连接到应用程序。如果你使用的是 Visual Studio,请在下拉列表中选择 Acme.BookStore.HttpApi.Host 而不是 IIS Express,如下图所示:

vs-run-without-iisexpress

一旦主机应用程序运行起来,请在 angular 文件夹中执行以下命令:

abp generate-proxy -t ng

此命令将在 /src/app/proxy/books 文件夹下创建以下文件:

Generated files

BookComponent

打开 /src/app/book/book.component.ts 文件,并将内容替换如下:

import { ListService, PagedResultDto } from '@abp/ng.core';
import { Component, OnInit, inject } 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>;

  public readonly list = inject(ListService);
  private readonly bookService = inject(BookService);

  ngOnInit() {
    const bookStreamCreator = (query) => this.bookService.getList(query);

    this.list.hookToQuery(bookStreamCreator).subscribe((response) => {
      this.book = response;
    });
  }
}
  • 我们导入并注入了生成的 BookService
  • 我们使用了 ListService,这是 ABP 提供的一个工具服务,可简化分页、排序和搜索。

打开 /src/app/book/book.component.html 并将内容替换为以下内容:

<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>
    </div>
  </div>
  <div class="card-body">
    <ngx-datatable [rows]="book.items" [count]="book.totalCount" [list]="list" default>
      <ngx-datatable-column [name]="'::Name' | abpLocalization" prop="name"></ngx-datatable-column>
      <ngx-datatable-column [name]="'::Type' | abpLocalization" prop="type">
        <ng-template let-row="row" ngx-datatable-cell-template>
          {{ '::Enum:BookType.' + row.type | abpLocalization }}
        </ng-template>
      </ngx-datatable-column>
      <ngx-datatable-column [name]="'::PublishDate' | abpLocalization" prop="publishDate">
        <ng-template let-row="row" ngx-datatable-cell-template>
          {{ row.publishDate | date }}
        </ng-template>
      </ngx-datatable-column>
      <ngx-datatable-column [name]="'::Price' | abpLocalization" prop="price">
        <ng-template let-row="row" ngx-datatable-cell-template>
          {{ row.price | currency }}
        </ng-template>
      </ngx-datatable-column>
    </ngx-datatable>
  </div>
</div>

现在你可以在浏览器中看到最终结果:

Book list final result

在本文档中