Abp vNext 学习第四弹 - 图书增删改查 参考上一篇 :Abp vNext 学习(3) .
新增图书 你将会了解如何创建一个 modal form 实现新增书籍的功能. model dialog将如下图所示:
在 Acme.BookStore.Web
项目的 Pages/Books
目录下新建一个 CreateModal.cshtml
Razor页面:
CreateModal.cshtml.cs 打开 CreateModal.cshtml.cs 代码文件(CreateModalModel 类),替换成以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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
而非默认的 PageModel
. BookStorePageModel
间接继承了 PageModel
并且添加了一些可以被你的page model类使用的通用属性和方法.
Book
属性上的 [BindProperty]
特性将post请求提交上来的数据绑定到该属性上.
该类通过构造函数注入了 IBookAppService
应用服务,并且在 OnPostAsync
处理程序中调用了服务的 CreateAsync
方法.
它在 OnGet
方法中创建一个新的 CreateUpdateBookDto
对象。 ASP.NET Core不需要像这样创建一个新实例就可以正常工作. 但是它不会为你创建实例,并且如果你的类在类构造函数中赋值一些默认值或执行一些代码,它们将无法工作. 对于这种情况,我们为某些 CreateUpdateBookDto
属性设置了默认值.
CreateModal.cshtml 打开 CreateModal.cshtml 文件并粘贴如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @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>
这个 modal
使用 abp-dynamic-form
tag Helper 根据 CreateUpdateBookDto
类自动构建了表单.
abp-model
指定了 Book
属性为模型对象.
abp-form-content
tag helper 作为表单控件渲染位置的占位符 (这是可选的,只有你在 abp-dynamic-form
中像本示例这样添加了其他内容才需要).
提示: 就像在本示例中一样,Layout 应该为 null,因为当通过AJAX加载模态窗口时,我们不希望包括所有布局.
添加 “New book” 按钮 打开 Pages/Books/Index.cshtml
并按如下代码修改 abp-card-header
:
1 2 3 4 5 6 7 8 9 10 <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-end" > <abp-button id ="NewBookButton" text ="@L[" NewBook "].Value " icon ="plus" button-type ="Primary" /> </abp-column > </abp-row > </abp-card-header >
如下图所示,只是在表格 右上方 添加了 New book 按钮:
打开 Pages/Book/Index.js
在 datatable
配置代码后面添加如下代码:
1 2 3 4 5 6 7 8 9 10 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
是一个在客户端管理modal的辅助类.它内部使用了Twitter Bootstrap的标准modal组件,但通过简化的API抽象了许多细节.
createModal.onResult(...)
用于在创建书籍后刷新数据表格.
createModal.open();
用于打开modal创建新书籍.
现在,你可以 运行程序 通过新的 modal form
来创建书籍了.
修改图书 在 Acme.BookStore.Web
项目的 Pages/Books
目录下新建一个名叫 EditModal.cshtml
的Razor页面:
EditModal.cshtml 将 EditModal.cshtml
页面内容替换成如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @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
非常相似,除了以下几点:
它包含id
属性的abp-input
, 用于存储被编辑书籍的 id
(它是隐藏的Input)
此页面指定的post
地址是Books/EditModal
.
EditModal.cshtml.cs 打开 EditModal.cshtml.cs 文件(EditModalModel类) 并替换成以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 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
从Http请求的查询字符串参数中获取Id的值.
在 OnGetAsync
方法中, 我们从 BookAppService
获得 BookDto
,并将它映射成DTO对象 CreateUpdateBookDto
.
OnPostAsync
方法直接使用 BookAppService.UpdateAsync
来更新实体.
BookDto 到 CreateUpdateBookDto 对象映射 为了执行 BookDto
到 CreateUpdateBookDto
对象映射,请打开 Acme.BookStore.Web
项目中的 BookStoreWebAutoMapperProfile.cs
并更改它,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 using AutoMapper;using Acme.BookStore.Books;namespace Acme.BookStore.Web { public class BookStoreWebAutoMapperProfile : Profile { public BookStoreWebAutoMapperProfile ( ) { CreateMap<BookDto, CreateUpdateBookDto>(); } } }
我们添加了 CreateMap<BookDto, CreateUpdateBookDto>();
作为映射定义.
请注意,我们在Web层中进行映射定义是一种最佳实践,因为仅在该层中需要它.
为表格添加 “操作(Actions)” 下拉菜单 我们将为表格每行添加下拉按钮 (“Actions”):
打开 Pages/Books/Index.js
页面,并按下方所示修改表格部分的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 $(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(); }); });
增加了一个新的 ModalManager
名为 editModal
打开编辑模态框.
在 columnDefs
部分的开头添加了一个新列,用于”Actions “下拉按钮.
“Edit “ 动作简单地调用 editModal.open()
打开编辑模态框.
editModal.onResult(...)
当你关闭编程模态框时进行回调刷新数据表格.
你可以运行应用程序,并通过选择一本书的编辑操作编辑任何一本书.
删除书籍 打开 Pages/book/index.js
文件,在 rowAction items
下新增一项:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 { 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请求删除一个book
.
abp.notify.info
执行删除操作后显示一个通知信息.
由于我们使用了两个新的本地化文本(BookDeletionConfirmationMessage和SuccesslyDeleted),因此你需要将它们添加到本地化文件(Acme.BookStore.Domain.Shared项目的Localization/BookStore文件夹下的en.json):
1 2 "BookDeletionConfirmationMessage" : "Are you sure to delete the book '{0}'?" ,"SuccessfullyDeleted" : "Successfully deleted!"
简体中文翻译请打开zh-Hans.json文件 ,并将”Texts”对象中对应的值替换为中文.
运行最终应用程序 你可以运行应用程序!该部分的最终用户界面如下所示:
Demo Codes
github commit
下一章 参阅的Abp vNext 学习(5) 。