Web应用程序开发教程 - 第九章: 作者: 用户页面
关于本教程
在本系列教程中, 你将构建一个名为 Acme.BookStore 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以下技术开发的:
- Entity Framework Core 做为ORM提供程序.
- Angular 做为UI框架.
本教程分为以下部分:
- Part 1: 创建服务端
- Part 2: 图书列表页面
- Part 3: 创建,更新和删除图书
- Part 4: 集成测试
- Part 5: 授权
- Part 6: 作者: 领域层
- Part 7: 作者: 数据库集成
- Part 8: 作者: 应用服务层
- Part 9: 作者: 用户页面 (本章)
- Part 10: 图书到作者的关系
下载源码
本教程根据你的UI 和 数据库偏好有多个版本,我们准备了几种可供下载的源码组合:
如果你在Windows中遇到 "文件名太长" or "解压错误", 很可能与Windows最大文件路径限制有关. Windows文件路径的最大长度为250字符. 为了解决这个问题,参阅 在Windows 10中启用长路径.
如果你遇到与Git相关的长路径错误, 尝试使用下面的命令在Windows中启用长路径. 参阅 https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path
git config --system core.longpaths true
简介
这章阐述如何为前一章介绍的 作者 实体创建CRUD页面.
作者管理页面
运行下面的命令行在angular应用程序的根目录创建一个名为 AuthorModule 新模块:
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.
AuthorModule
打开 /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 APIs生成客户端代理, 使得使用HTTP APIs更容易. generate-proxy 命令运行前, 应用程序必须启动运行.
在 angular 文件夹运行以下命令:
abp generate-proxy -t ng
这个命令为作者服务和相关模型(DTO)类生成服务代理:
编辑模态窗口
在 Acme.BookStore.Web 项目的 Pages/Authors 文件夹下创建一个 razor 页面 EditModal.cshtml, 修改它的内容如下:
EditModal.cshtml
@page
@using Acme.BookStore.Localization
@using Acme.BookStore.Web.Pages.Authors
@using Microsoft.Extensions.Localization
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model EditModalModel
@inject IStringLocalizer<BookStoreResource> L
@{
Layout = null;
}
<form asp-page="/Authors/EditModal">
<abp-modal>
<abp-modal-header title="@L["Update"].Value"></abp-modal-header>
<abp-modal-body>
<abp-input asp-for="Author.Id" />
<abp-input asp-for="Author.Name" />
<abp-input asp-for="Author.BirthDate" />
<abp-input asp-for="Author.ShortBio" />
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer>
</abp-modal>
</form>
EditModal.cshtml.cs
using System;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Acme.BookStore.Authors;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
namespace Acme.BookStore.Web.Pages.Authors
{
public class EditModalModel : BookStorePageModel
{
[BindProperty]
public EditAuthorViewModel Author { get; set; }
private readonly IAuthorAppService _authorAppService;
public EditModalModel(IAuthorAppService authorAppService)
{
_authorAppService = authorAppService;
}
public async Task OnGetAsync(Guid id)
{
var authorDto = await _authorAppService.GetAsync(id);
Author = ObjectMapper.Map<AuthorDto, EditAuthorViewModel>(authorDto);
}
public async Task<IActionResult> OnPostAsync()
{
await _authorAppService.UpdateAsync(
Author.Id,
ObjectMapper.Map<EditAuthorViewModel, UpdateAuthorDto>(Author)
);
return NoContent();
}
public class EditAuthorViewModel
{
[HiddenInput]
public Guid Id { get; set; }
[Required]
[StringLength(AuthorConsts.MaxNameLength)]
public string Name { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime BirthDate { get; set; }
[TextArea]
public string ShortBio { get; set; }
}
}
}
这个类与 CreateModal.cshtml.cs 类似, 主要不同是:
- 使用
IAuthorAppService.GetAsync(...)方法从应用层获取正在编辑的作者. EditAuthorViewModel拥有一个额外的Id属性, 它被[HiddenInput]特性标记, 会为这个属性在页面上创建一个隐藏输入框.
这个类要求在 BookStoreWebAutoMapperProfile 类中添加两个对象映射声明:
using Acme.BookStore.Authors;
using Acme.BookStore.Books;
using AutoMapper;
namespace Acme.BookStore.Web
{
public class BookStoreWebAutoMapperProfile : Profile
{
public BookStoreWebAutoMapperProfile()
{
CreateMap<BookDto, CreateUpdateBookDto>();
CreateMap<Pages.Authors.CreateModalModel.CreateAuthorViewModel,
CreateAuthorDto>();
// ADD THESE NEW MAPPINGS
CreateMap<AuthorDto, Pages.Authors.EditModalModel.EditAuthorViewModel>();
CreateMap<Pages.Authors.EditModalModel.EditAuthorViewModel,
UpdateAuthorDto>();
}
}
}
这就是全部了! 你可以运行应用程序并尝试编辑一个作者.
下一章
查看本教程的下一章.
抠丁客


