编辑产品类似于添加新产品,现在让我们看看如何编辑产品:
## 定义应用接口
让我们从为`IProductAppService`接口定义两个新方法:
```
Task<ProductDto> GetAsync(Guid id);
Task UpdateAsync(Guid id, CreateUpdateProductDto input);
```
第一种方法用于通过ID获取产品。我们在`UpdateAsync`方法中重用之前定义的`CreateUpdateProductDto`。
## 实现应用接口
实现这些新方法非常简单。将以下方法添加到`ProductAppService`类中:
```
public async Task<ProductDto> GetAsync(Guid id)
{
return ObjectMapper.Map<Product, ProductDto>(
await _productRepository.GetAsync(id)
);
}
public async Task UpdateAsync(Guid id, CreateUpdateProductDto input)
{
var product = await _productRepository.GetAsync(id);
ObjectMapper.Map(input, product);
}
```
`GetAsync`方法用于从数据库中获取产品,并将其映射到`ProductDto`对象后进行返回。`UpdateAsync`方法获取到一个产品后,将给定的DTO输入映射到产品。通过这种方式,我们用新值覆盖产品。
对于这个例子,我们不需要调用`_productRepository.UpdateAsync`,因为 EF Core有一个变更跟踪系统。ABP 的**工作单元**如果没有抛出异常,则在请求结束时会自动保存更改。我们将在\[*第 6 章*\] \*使用数据访问基础架构”\*中介绍工作单元系统。
应用层已完成。接下来,我们将创建一个产品编辑 UI。
## 用户界面
创建一个`EditProductModal.cshtml`Razor 页面(*ProductManagement.Web项目的* *Pages/Products*文件夹下)。打开`EditProductModal.cshtml.cs`,代码更改如下:
```
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using ProductManagement.Products;
namespace ProductManagement.Web.Pages.Products
{
public class EditProductModalModel : ProductManagementPageModel
{
[HiddenInput]
[BindProperty(SupportsGet = true)]
public Guid Id { get; set; }
[BindProperty]
public CreateEditProductViewModel Product { get; set; }
public SelectListItem[] Categories { get; set; }
private readonly IProductAppService _productAppService;
public EditProductModalModel(IProductAppService productAppService)
{
_productAppService = productAppService;
}
public async Task OnGetAsync()
{
// TODO
}
public async Task<IActionResult> OnPostAsync()
{
// TODO
}
}
}
```
表单中`Id`字段将被隐藏。
它还应该支持 HTTP GET 请求,因为 GET 请求会打开此模型,并且我们需要产品 ID 来编辑表单。
`Product`和`Categories`属性类似于创建产品。
我们还将`IProductAppService`接口注入到构造函数。
我们实现`OnGetAsync`方法,如下代码块所示:
```
public async Task OnGetAsync()
{
var productDto = await _productAppService.GetAsync(Id);
Product = ObjectMapper.Map<ProductDto, CreateEditProductViewModel>(productDto);
var categoryLookup = await _productAppService.GetCategoriesAsync();
Categories = categoryLookup.Items
.Select(x => new SelectListItem(x.Name, x.Id.ToString()))
.ToArray();
}
```
首先,我们要先获取一个产品 ( `ProductDto`),再将其转换为`CreateEditProductViewModel`,使用它在 UI 上来创建编辑表单。然后,我们在表单上选择产品类别。
因为这里映射了`ProductDto`到`CreateEditProductViewModel`,所以我们需要在`ProductManagementWebAutoMapperProfile`类中定义配置映射([*ProductManagement.Web*](http://ProductManagement.Web)项目中),这和我们之前操作是一样的:
```
CreateMap<ProductDto, CreateEditProductViewModel>();
```
我们再看下`OnPostAsync()`方法:
```
public async Task<IActionResult> OnPostAsync()
{
await _productAppService.UpdateAsync(Id,
ObjectMapper.Map<CreateEditProductViewModel, CreateUpdateProductDto>(Product)
);
return NoContent();
}
```
`OnPostAsync`方法很简单,把`CreateEditProductViewModel`转换为`CreateUpdateProductDto`。
接着,我们切换到`EditProductModal.cshtml`,内容更改如下:
```
@page
@using Microsoft.AspNetCore.Mvc.Localization
@using ProductManagement.Localization
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model ProductManagement.Web.Pages.Products.EditProductModalModel
@inject IHtmlLocalizer<ProductManagementResource> L
@{
Layout = null;
}
<abp-dynamic-form abp-model="Product" asp-page="/Products/EditProductModal">
<abp-modal>
<abp-modal-header title="@Model.Product.Name"></abp-modal-header>
<abp-modal-body>
<abp-input asp-for="Id" />
<abp-form-content/>
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer>
</abp-modal>
</abp-dynamic-form>
```
页面与`CreateProductModal.cshtml`非常相似。我刚刚将`Id`字段作为隐藏字段添加到表单,用来存储`Id`编辑的产品的属性。
最后,我们可以添加一个**编辑**按钮以从产品列表中打开编辑模态窗口。打开`Index.cshtml.js`文件,并在`dataTable`代码的头部添加一个`ModalManager`对象:
```
var editModal = new abp.ModalManager(abp.appPath + 'Products/EditProductModal');
```
然后,在`dataTable`内部的`columnDefs`数组中定义一个列(第一项):
```
{
title: l('Actions'),
rowAction: {
items:
[
{
text: l('Edit'),
action: function (data) {
editModal.open({ id: data.record.id });
}
}
]
}
},
```
此代码向数据表添加了一个新的**Actions**列,并添加了一个**Edit**操作按钮,单击即可打开编辑窗口。`rowAction`是 ABP Framework 提供的一个特殊选项。它用于在表中的一行添加一个或多个操作按钮。
最后,在`dataTable`初始化代码后添加如下:
```
editModal.onResult(function () {
dataTable.ajax.reload();
});
```
在保存产品编辑对话框后刷新数据表,确保我们可以看到表上的最新数据。最终的 UI 类似于下图:
![](https://img.kancloud.cn/29/82/2982358d4679a7eb3964b8ec6990e1db_1116x664.png)
我们现在可以查看、创建和编辑产品了。最后一部分将实现删除产品。
- 前言
- 第一部分
- 第1章 现代软件开发和 ABP 框架
- 企业级 Web 开发的挑战
- ABP框架的能力清单
- 第2章 ABP框架入门
- 安装 ABP CLI
- 创建新解决方案
- 运行解决方案
- 探索预构建模块
- 第3章 逐步开发开发ABP应用
- 创建解决方案
- 定义领域对象
- EFCore和数据库映射
- 定义应用服务
- 测试产品
- 产品列表
- 创建产品
- 编辑产品
- 删除产品
- 第4章 探索 EventHub解决方案
- 应用介绍
- 架构探索
- 方案运行
- 第二部分
- 第5章 探索ABP基础架构
- 了解模块化
- 使用依赖注入系统
- 配置应用程序
- 实现选项模式
- 日志系统
- 第6章 数据访问基础架构
- 定义实体
- 定义仓储库
- EF Core集成
- 了解 UoW
- 第7章 探索横切关注点
- 认证授权
- 用户验证
- 异常处理
- 第8章 体验 ABP 的功能和服务
- 获取当前用户
- 使用数据过滤
- 控制审计日志
- 缓存数据
- 本地化用户界面
- 第三部分
- 第9章 理解领域驱动设计
- 介绍 DDD
- 构建基于 DDD 的 解决方案
- 处理多个应用程序
- 了解执行流程
- DDD的通用原则
- 第10章 领域层 Domain
- 领域事件案例分析
- 聚合和实体的设计原则和实践
- 实现领域服务
- 落地存储库
- 构建规约(Specification)
- 领域事件
- 第11章 应用层 Application
- 落地应用服务
- 设计 DTO
- 理解各层的职责
- 第四部分
- 第12章 MVC/Razor 页面
- 主题系统
- 绑定和压缩
- 导航菜单
- Bootstrap标签助手
- 创建表单并验证
- 使用模态窗口
- 使用JS API
- 调用HTTP API
- 第13章 Blazor WebAssembly UI
- 什么是Blazor
- ABP Blazor UI
- 验证用户身份
- 理解主题系统
- 使用菜单
- 使用基本服务
- 使用UI服务
- 消费HTTP API
- 使用全局脚本和样式
- 第14章 HTTP API 和实时服务
- 构建HTTP API
- 使用HTTP API
- 使用SignalR
- 第五部分
- 第15章 落地模块化
- 理解模块化
- 构建支付模块
- 安装模块
- 第16章 实现多租户
- 理解多租户
- 多租户基础设施
- 使用功能系统
- 何时使用多租户
- 第17章 构建自动化测试
- 了解ABP测试基础设施
- 构建单元测试
- 构建集成测试