文章目录

  • 关于本教程
    • 下载源代码
  • 创建新书
    • 创建模态表单
    • 添加“新书”按钮
  • 更新书
    • EditModal.cshtml.cs
    • 从BookDto映射到CreateUpdateBookDto
    • EditModal.cshtml
    • 为表格添加 "操作(Actions)" 下拉菜单
  • 删除书
  • 下一部分

关于本教程

本教程基于版本3.1

在本教程系列中,您将构建一个名为Acme.BookStore的基于ABPWeb应用程序。该应用程序用于管理书籍及其作者的列表。它是使用以下技术开发的:

  • 实体框架核心作为ORM提供者。
  • MVC/Razor页面作为UI框架。

本教程分为以下部分:

第1部分:创建服务器端

第2部分:图书列表页面

第3部分:创建、更新和删除书籍(此部分)

第4部分:集成测试

第5部分:授权

第6部分:作者:领领域层

第7部分:作者:数据库集成

第8部分:作者:应用程序层

第9部分:作者:用户界面

第10部分:书与作者的关系

下载源代码

MVC (Razor Pages) UI with EF Core

创建新书

在本节中,您将学习如何创建新的模态对话框形式来创建新书。模态对话框如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GZKn09IM-1601124148452)(bookstore-create-dialog-2.png)]

创建模态表单

创建一个名为CreateModal.cshtml新的razor页面,在Acme.BookStore.Web项目Pages/Books的文件夹下。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HH4l1JWn-1601124148453)(bookstore-add-create-dialog-v2.png)]

CreateModal.cshtml.cs

打开CreateModal.cshtml.cs文件(CreateModalModel类)并替换为以下代码:

using System.Threading.Tasks;
using Acme.BookStore.Books;
using Microsoft.AspNetCore.Mvc;namespace Acme.BookStore.Web.Pages.Books
{public class CreateModalModel : BookStorePageModel{[BindProperty]public CreateUpdateBookDto Book { get; set; }private readonly IBookAppService _bookAppService;public CreateModalModel(IBookAppService bookAppService){_bookAppService = bookAppService;}public void OnGet(){Book = new CreateUpdateBookDto();}public async Task<IActionResult> OnPostAsync(){await _bookAppService.CreateAsync(Book);return NoContent();}}
}
  • 此类派生自BookStorePageModel而不是标准的PageModelBookStorePageModel间接继承自PageModel,并添加一些可以在页面模型类中共享的常用属性和方法。

  • Book属性上的[BindProperty]特性将post请求数据绑定到该属性。

  • 此类仅将IBookAppService注入到构造函数中,并在OnPostAsync处理程序中调用CreateAsync方法。

  • 它在OnGet方法中创建一个新CreateUpdateBookDto对象。ASP.NET Core可以正常工作,而无需创建这样的新实例。但是,它不会为您创建实例,并且如果您的类在类构造函数中有一些默认值分配或代码执行,它们将无法工作。对于这个例子,我们为某些CreateUpdateBookDto属性设置了默认值。

CreateModal.cshtml

打开CreateModal.cshtml文件并粘贴以下代码:

@page
@using Acme.BookStore.Localization
@using Acme.BookStore.Web.Pages.Books
@using Microsoft.Extensions.Localization
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model CreateModalModel
@inject IStringLocalizer<BookStoreResource> L
@{Layout = null;
}
<abp-dynamic-form abp-model="Book" asp-page="/Books/CreateModal"><abp-modal><abp-modal-header title="@L["NewBook"].Value"></abp-modal-header><abp-modal-body><abp-form-content /></abp-modal-body><abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer></abp-modal>
</abp-dynamic-form>
  • 该模式使用abp-dynamic-form标签帮助程序从CreateBookViewModel模型类自动创建表单 。

  • 在这种情况下,abp-model attribute指示模型对象所在的Book属性。

  • abp-form-content标记帮助程序是呈现表单控件的占位符(仅当您在abp-dynamic-form标记中添加了其他内容(如此页面一样)时,它才是可选的,并且是必需的)。

提示:Layout应该是null,像在这个例子中做的那样,因为当通过AJAX加载时,我们不希望包括模态(modals)的所有布局。

添加“新书”按钮

打开Pages/Books/Index.cshtml并设置abp-card-header标签内容,如下所示:

<abp-card-header><abp-row><abp-column size-md="_6"><abp-card-title>@L["Books"]</abp-card-title></abp-column><abp-column size-md="_6" class="text-right"><abp-button id="NewBookButton"text="@L["NewBook"].Value"icon="plus"button-type="Primary"/></abp-column></abp-row>
</abp-card-header>

Index.cshtml的最终内容如下所示:

@page
@using Acme.BookStore.Localization
@using Acme.BookStore.Web.Pages.Books
@using Microsoft.Extensions.Localization
@model IndexModel
@inject IStringLocalizer<BookStoreResource> L
@section scripts
{<abp-script src="/Pages/Books/Index.js"/>
}<abp-card><abp-card-header><abp-row><abp-column size-md="_6"><abp-card-title>@L["Books"]</abp-card-title></abp-column><abp-column size-md="_6" class="text-right"><abp-button id="NewBookButton"text="@L["NewBook"].Value"icon="plus"button-type="Primary"/></abp-column></abp-row></abp-card-header><abp-card-body><abp-table striped-rows="true" id="BooksTable"></abp-table></abp-card-body>
</abp-card>

这将在表格的右上角添加一个名为New book的新按钮:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CjWfKqS6-1601124148454)(bookstore-new-book-button-2.png)]

打开Pages/Books/Index.js并在Datatable配置之后添加以下代码:

var createModal = new abp.ModalManager(abp.appPath + 'Books/CreateModal');createModal.onResult(function () {dataTable.ajax.reload();
});$('#NewBookButton').click(function (e) {e.preventDefault();createModal.open();
});
  • abp.ModalManager是在客户端管理模态的助手类。它在内部使用Twitter Bootstrap的标准模态,但是通过提供一个简单的API来抽象许多细节。

  • createModal.onResult(...) 用于在创建新书后刷新数据表。

  • createModal.open(); 用于打开模型以创建新书。

Index.js的最终内容应为:

$(function () {var l = abp.localization.getResource('BookStore');var dataTable = $('#BooksTable').DataTable(abp.libs.datatables.normalizeConfiguration({serverSide: true,paging: true,order: [[1, "asc"]],searching: false,scrollX: true,ajax: abp.libs.datatables.createAjax(acme.bookStore.books.book.getList),columnDefs: [{title: l('Name'),data: "name"},{title: l('Type'),data: "type",render: function (data) {return l('Enum:BookType:' + data);}},{title: l('PublishDate'),data: "publishDate",render: function (data) {return luxon.DateTime.fromISO(data, {locale: abp.localization.currentCulture.name}).toLocaleString();}},{title: l('Price'),data: "price"},{title: l('CreationTime'), data: "creationTime",render: function (data) {return luxon.DateTime.fromISO(data, {locale: abp.localization.currentCulture.name}).toLocaleString(luxon.DateTime.DATETIME_SHORT);}}]}));var createModal = new abp.ModalManager(abp.appPath + 'Books/CreateModal');createModal.onResult(function () {dataTable.ajax.reload();});$('#NewBookButton').click(function (e) {e.preventDefault();createModal.open();});
});

现在,您可以运行该应用程序,并使用新的模态形式添加一些新书。

更新书

创建一个名为EditModal.cshtml新的razor页面,在Acme.BookStore.Web项目下Pages/Books的文件夹:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ush3qQLy-1601124148457)(bookstore-add-edit-dialog.png)]

EditModal.cshtml.cs

打开EditModal.cshtml.cs文件(EditModalModel类)并替换为以下代码:

using System;
using System.Threading.Tasks;
using Acme.BookStore.Books;
using Microsoft.AspNetCore.Mvc;namespace Acme.BookStore.Web.Pages.Books
{public class EditModalModel : BookStorePageModel{[HiddenInput][BindProperty(SupportsGet = true)]public Guid Id { get; set; }[BindProperty]public CreateUpdateBookDto Book { get; set; }private readonly IBookAppService _bookAppService;public EditModalModel(IBookAppService bookAppService){_bookAppService = bookAppService;}public async Task OnGetAsync(){var bookDto = await _bookAppService.GetAsync(Id);Book = ObjectMapper.Map<BookDto, CreateUpdateBookDto>(bookDto);}public async Task<IActionResult> OnPostAsync(){await _bookAppService.UpdateAsync(Id, Book);return NoContent();}}
}
  • [HiddenInput][BindProperty]是标准的ASP.NET Core MVC的属性。SupportsGet用于能够从请求的查询字符串参数获取Id值。

  • OnGetAsync方法中,我们BookAppService从获取BookDto,并将其映射到DTO对象CreateUpdateBookDto

  • OnPostAsync使用BookAppService.UpdateAsync(...)更新实体。

从BookDto映射到CreateUpdateBookDto

为了能够将BookDto映射到CreateUpdateBookDto,请配置新的映射。为此,请在Acme.BookStore.Web项目中打开BookStoreWebAutoMapperProfile.cs并按如下所示进行更改:

using AutoMapper;namespace Acme.BookStore.Web
{public class BookStoreWebAutoMapperProfile : Profile{public BookStoreWebAutoMapperProfile(){CreateMap<BookDto, CreateUpdateBookDto>();}}
}
  • 我们刚刚添加了CreateMap<BookDto, CreateUpdateBookDto>();以定义此映射。

注意,由于我们仅在该层中需要映射定义,因此我们将其作为在Web层中的最佳实践进行。

EditModal.cshtml

用以下内容替换EditModal.cshtml内容:

@page
@using Acme.BookStore.Localization
@using Acme.BookStore.Web.Pages.Books
@using Microsoft.Extensions.Localization
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model EditModalModel
@inject IStringLocalizer<BookStoreResource> L
@{Layout = null;
}
<abp-dynamic-form abp-model="Book" asp-page="/Books/EditModal"><abp-modal><abp-modal-header title="@L["Update"].Value"></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>

该页面与十分相似CreateModal.cshtml,除了:

  • 它包括一个abp-input用于Id存储Id编辑书的属性(这是一个隐藏的输入)。

  • 它Books/EditModal用作发布URL。

为表格添加 “操作(Actions)” 下拉菜单

我们将在名为Actions的表中添加一个下拉按钮。

打开Pages/Books/Index.js并替换以下内容:

$(function () {var l = abp.localization.getResource('BookStore');var createModal = new abp.ModalManager(abp.appPath + 'Books/CreateModal');var editModal = new abp.ModalManager(abp.appPath + 'Books/EditModal');var dataTable = $('#BooksTable').DataTable(abp.libs.datatables.normalizeConfiguration({serverSide: true,paging: true,order: [[1, "asc"]],searching: false,scrollX: true,ajax: abp.libs.datatables.createAjax(acme.bookStore.books.book.getList),columnDefs: [{title: l('Actions'),rowAction: {items:[{text: l('Edit'),action: function (data) {editModal.open({ id: data.record.id });}}]}},{title: l('Name'),data: "name"},{title: l('Type'),data: "type",render: function (data) {return l('Enum:BookType:' + data);}},{title: l('PublishDate'),data: "publishDate",render: function (data) {return luxon.DateTime.fromISO(data, {locale: abp.localization.currentCulture.name}).toLocaleString();}},{title: l('Price'),data: "price"},{title: l('CreationTime'), data: "creationTime",render: function (data) {return luxon.DateTime.fromISO(data, {locale: abp.localization.currentCulture.name}).toLocaleString(luxon.DateTime.DATETIME_SHORT);}}]}));createModal.onResult(function () {dataTable.ajax.reload();});editModal.onResult(function () {dataTable.ajax.reload();});$('#NewBookButton').click(function (e) {e.preventDefault();createModal.open();});
});
  • 添加了一个新的命名为editModalModalManager以打开编辑模态对话框。

  • 在本columnDefs节的开头添加了新列。此列用于“操作(Actions)”下拉按钮。

  • “Edit”操作只需调用editModal.open()即可打开编辑对话框。

  • 关闭编辑模态时,editModal.onResult(...)回调会刷新数据表。

您可以通过选择一本书的编辑操作来运行该应用程序并编辑任何一本书。

最终的UI如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UWznCP1U-1601124148458)(bookstore-edit-button-2.png)]

删除书

打开Pages/Books/Index.js并将新项添加到rowAction items

{text: l('Delete'),confirmMessage: function (data) {return l('BookDeletionConfirmationMessage', data.record.name);},action: function (data) {acme.bookStore.books.book.delete(data.record.id).then(function() {abp.notify.info(l('SuccessfullyDeleted'));dataTable.ajax.reload();});}
}
  • confirmMessage选项用于在执行action之前询问确认问题。

  • acme.bookStore.books.book.delete(...) 方法向服务器发出AJAX请求以删除一本书。

  • abp.notify.info() 显示删除操作后的通知。

由于我们使用了两个新的本地化文本(BookDeletionConfirmationMessageSuccessfullyDeleted),因此需要将它们添加到本地化文件中(在Acme.BookStore.Domain.Shared项目Localization/BookStore文件夹下的en.json中):

"BookDeletionConfirmationMessage": "Are you sure to delete the book '{0}'?",
"SuccessfullyDeleted": "Successfully deleted!"

最终Index.js内容如下所示:

$(function () {var l = abp.localization.getResource('BookStore');var createModal = new abp.ModalManager(abp.appPath + 'Books/CreateModal');var editModal = new abp.ModalManager(abp.appPath + 'Books/EditModal');var dataTable = $('#BooksTable').DataTable(abp.libs.datatables.normalizeConfiguration({serverSide: true,paging: true,order: [[1, "asc"]],searching: false,scrollX: true,ajax: abp.libs.datatables.createAjax(acme.bookStore.books.book.getList),columnDefs: [{title: l('Actions'),rowAction: {items:[{text: l('Edit'),action: function (data) {editModal.open({ id: data.record.id });}},{text: l('Delete'),confirmMessage: function (data) {return l('BookDeletionConfirmationMessage',data.record.name);},action: function (data) {acme.bookStore.books.book.delete(data.record.id).then(function() {abp.notify.info(l('SuccessfullyDeleted'));dataTable.ajax.reload();});}}]}},{title: l('Name'),data: "name"},{title: l('Type'),data: "type",render: function (data) {return l('Enum:BookType:' + data);}},{title: l('PublishDate'),data: "publishDate",render: function (data) {return luxon.DateTime.fromISO(data, {locale: abp.localization.currentCulture.name}).toLocaleString();}},{title: l('Price'),data: "price"},{title: l('CreationTime'), data: "creationTime",render: function (data) {return luxon.DateTime.fromISO(data, {locale: abp.localization.currentCulture.name}).toLocaleString(luxon.DateTime.DATETIME_SHORT);}}]}));createModal.onResult(function () {dataTable.ajax.reload();});editModal.onResult(function () {dataTable.ajax.reload();});$('#NewBookButton').click(function (e) {e.preventDefault();createModal.open();});
});

您可以运行该应用程序并尝试删除一本书。

下一部分

请参阅本教程的下一部分。

Abp vnext Web应用程序开发教程 3 —— 创建、更新和删除书籍相关推荐

  1. Abp vnext Web应用程序开发教程 1 —— 创建服务器端

    文章目录 关于本教程 下载源代码 创建解决方案 创建书籍实体 BookType枚举 将图书实体添加到DbContext 将图书实体映射到数据库表 添加数据库迁移 添加样本种子数据 更新数据库 创建应用 ...

  2. Abp vnext Web应用程序开发教程 10 —— 书与作者的关系

    文章目录 关于本教程 下载源代码 介绍 向书实体添加关系 数据库和数据迁移 更新EF核心映射 添加新的EF核心迁移 更改数据播种器 应用层 数据传输对象 IBookAppService BookApp ...

  3. Abp vnext Web应用程序开发教程 9 —— 作者:用户界面

    文章目录 关于本教程 下载源代码 介绍 图书列表页面 Index.cshtml IndexModel.cshtml.cs Index.js 本地化 添加到主菜单 运行应用程序 创建模态 CreateM ...

  4. Abp vnext Web应用程序开发教程 8 —— 作者:应用程序层

    文章目录 关于本教程 下载源代码 介绍 IAuthorAppService AuthorDto GetAuthorListDto CreateAuthorDto UpdateAuthorDto Aut ...

  5. Abp vnext Web应用程序开发教程 7 —— 作者:数据库集成

    文章目录 关于本教程 下载源代码 介绍 数据库上下文 创建一个新的数据库迁移 实现IAuthorRepository 下一部分 关于本教程 本教程基于版本3.1 在本教程系列中,您将构建一个名为Acm ...

  6. Abp vnext Web应用程序开发教程 6 —— 作者:领域层

    文章目录 关于本教程 下载源代码 介绍 作者实体 AuthorManager:领域服务 IAuthorRepository 结论 下一部分 关于本教程 本教程基于版本3.1 在本教程系列中,您将构建一 ...

  7. Abp vnext Web应用程序开发教程 4 —— 集成测试

    文章目录 关于本教程 下载源代码 在解决方案中测试项目 添加测试数据 测试BookAppService 下一部分 关于本教程 本教程基于版本3.1 在本教程系列中,您将构建一个名为Acme.BookS ...

  8. Abp vnext Web应用程序开发教程 2 —— 图书列表页面

    文章目录 关于本教程 下载源代码 动态JavaScript代理 在开发者控制台中进行测试 本地化 创建书籍页面 将书籍页面添加到主菜单 图书列表 运行最终应用程序 下一部分 关于本教程 本教程基于版本 ...

  9. Abp vnext Web应用程序开发教程 5 —— 授权

    文章目录 关于本教程 下载源代码 权限 权限名称 权限定义 权限管理界面 授权 应用层和HTTP API Razor页面 JavaScript端 菜单项 下一部分 关于本教程 本教程基于版本3.1 在 ...

最新文章

  1. 细节决定成败--打电话和发邮件的细节
  2. 多线程:并发实现方法之J.U.C
  3. Smarty2至Smarty3升级指南
  4. calendar类计算时间距离_日期时间--JAVA成长之路
  5. CUDA C编程权威指南 第三章 CUDA执行模型
  6. 网页左右怎么划分_UI基础汇总——网页设计规范
  7. 微软官方SqlHelper
  8. java 二维数组内存溢出_程序员:学习心得,Java内存区域,内存溢出异常
  9. 作为一个php程序员要学会的技能
  10. 点菜单项在面板中打开并判断是否打开
  11. 【光学】基于matlab实现圆孔的菲涅尔衍射仿真
  12. wincc工程组态论文_2020/12/18【推荐】几种常用工控组态软件介绍,这次找齐了,你懂哪种?...
  13. mysql随机生成中文姓名_编写mysql函数 随机生成中文姓名
  14. priority inversion
  15. 金之塔用 Python 获取日内分时均价,每分钟日成交量和每时刻结算价,及交叉作用曲线延伸
  16. 软件加壳的简易实现方式
  17. 为什么说阿里云和亚马逊云对比,阿里云的性价比比较低?
  18. 11091 最优自然数分解问题
  19. 常见六种热量高食物和饮料
  20. 黑马程序员中的简单网页制作

热门文章

  1. php 最长公共子串,PHP实现求解最长公共子串思路方法
  2. ninjala还是显示服务器维护,Ninjala更新2.0版本 调整了武器平衡以及BUG修复
  3. 写python代码的心得体会_写python代码的一点感想
  4. python程序如何做界面_如何用Python给已有小程序做界面?
  5. vb里面计算误差咱们解决_盘点3dmax渲染出来是黑色的6个原因及解决方法
  6. (C++)wchar_t 转 string / TCHAR转为char/判断是否进程运行/获得目标进程的入口
  7. QT创建文件夹(QDir方式)
  8. ASN.1编解码:asn1c-ORAN-E2AP编解码示例
  9. 实地址模式下的中断向量表
  10. The Real Time Linux and RT Applications | PREEMPT_RT