文章目录
关于本教程下载源代码动态JavaScript代理在开发者控制台中进行测试本地化创建书籍页面将书籍页面添加到主菜单图书列表运行最终应用程序下一部分关于本教程
本教程基于版本3.1
在本教程系列中,您将构建一个名为Acme.BookStore
的基于ABP的Web应用程序。该应用程序用于管理书籍及其作者的列表。它是使用以下技术开发的:
实体框架核心作为ORM提供者。MVC/Razor页面作为UI框架。
本教程分为以下部分:
第1部分:创建服务器端
第2部分:图书列表页面(此部分)
第3部分:创建、更新和删除书籍
第4部分:集成测试
第5部分:授权
第6部分:作者:领领域层
第7部分:作者:数据库集成
第8部分:作者:应用程序层
第9部分:作者:用户界面
第10部分:书与作者的关系
下载源代码
MVC (Razor Pages) UI with EF Core
动态JavaScript代理
从JavaScript端通过AJAX
调用HTTP API
端点是很常见的。您可以使用$.ajax
或其他工具来调用端点。但是,ABP提供了更好的方法。
ABP 为所有API端点动态创建JavaScript代理。因此,您可以使用任何端点,就像调用JavaScript函数一样。
在开发者控制台中进行测试
您可以使用自己喜欢的浏览器的开发者控制台轻松测试JavaScript代理
。运行该应用程序,打开浏览器的开发人员工具(快捷方式通常为F12),切换到“控制台”选项卡,键入以下代码,然后按Enter
:
acme.bookStore.books.book.getList({}).done(function (result) { console.log(result); });
acme.bookStore.books
是BookAppService
转换为camelCase的名称空间。
book
是BookAppService
(已删除AppService
的后缀并转换为camelCase
)的常规名称。
getList
是在CrudAppService
基类中GetListAsync
定义的方法的常规名称(删除Async
后缀并转换为camelCase
)。
{}
参数用于将空对象发送给GetListAsync
方法,其通常需要类型PagedAndSortedResultRequestDto
的对象,该对象用于将分页和排序选项发送至服务器(所有属性都是可选的,具有默认值,因此您可以发送空对象)。
getList
函数返回promise
。您可以将回调传递给then
(或done
)函数,以获取服务器返回的结果。
运行此代码将产生以下输出:
您可以看到从服务器返回的图书列表。您还可以检查开发人员工具的“网络”标签以查看客户端与服务器之间的通信:
让我们使用create
函数创建一本新书:
acme.bookStore.books.book.create({name: 'Foundation', type: 7, publishDate: '1951-05-24', price: 21.5 }).then(function (result) {console.log('successfully created the book with id: ' + result.id); });
您应该在控制台中看到类似以下的消息:
successfully created the book with id: 439b0ea8-923e-8e1e-5d97-39f2c7ac4246
检查数据库中的Books
表以查看一行新书记录。你可以自己试试get
,update
和delete
功能。
在下一部分中,我们将使用这些动态代理功能与服务器进行通信。
本地化
在开始UI开发之前,我们首先要准备本地化文本(通常在开发应用程序时需要时执行)。
本地化文本位于Acme.BookStore.Domain.Shared
项目的Localization/BookStore
文件夹下:
打开en.json
(英文翻译)文件,然后更改内容,如下所示:
{"Culture": "en","Texts": {"Menu:Home": "Home","Welcome": "Welcome","LongWelcomeMessage": "Welcome to the application. This is a startup project based on the ABP framework. For more information, visit abp.io.","Menu:BookStore": "Book Store","Menu:Books": "Books","Actions": "Actions","Close": "Close","Delete": "Delete","Edit": "Edit","PublishDate": "Publish date","NewBook": "New book","Name": "Name","Type": "Type","Price": "Price","CreationTime": "Creation time","AreYouSure": "Are you sure?","AreYouSureToDelete": "Are you sure you want to delete this item?","Enum:BookType:0": "Undefined","Enum:BookType:1": "Adventure","Enum:BookType:2": "Biography","Enum:BookType:3": "Dystopia","Enum:BookType:4": "Fantastic","Enum:BookType:5": "Horror","Enum:BookType:6": "Science","Enum:BookType:7": "Science fiction","Enum:BookType:8": "Poetry"}}
本地化关键字名称是任意的。您可以设置任何名称。对于特定的文本类型,我们更喜欢一些约定;
为菜单项添加Menu:
前缀。使用Enum:<enum-type>:<enum-value>
命名约定对枚举成员进行本地化。当您这样做时,ABP可以在某些适当的情况下自动将枚举本地化。
如果未在本地化文件中定义文本,则文本将回退到本地化键(作为Core
的标准行为)。
ABP的本地化系统基于 Core的标准本地化系统,并以多种方式进行了扩展。有关详细信息,请参见本地化文档。
创建书籍页面
是时候创建可见的和可用的东西了!代替经典的MVC,我们将使用Microsoft建议的Razor Pages UI方法。
在Acme.BookStore.Web
项目Books
文件夹下创建Pages
文件夹。右键单击“Books”文件夹,然后选择“添加”>“Razor页面”菜单项,以添加新的Razor页面。命名为Index
:
打开Index.cshtml
并更改整个内容,如下所示:
@page@using Acme.BookStore.Web.Pages.Books@model IndexModel<h2>Books</h2>
Index.cshtml.cs
内容应该是这样的:
using Microsoft.AspNetCore.Mvc.RazorPages;namespace Acme.BookStore.Web.Pages.Books{public class IndexModel : PageModel{public void OnGet(){}}}
将书籍页面添加到主菜单
打开Menus
文件夹中的BookStoreMenuContributor
类,并将以下代码添加到ConfigureMainMenuAsync
方法的末尾:
context.Menu.AddItem(new ApplicationMenuItem("BooksStore",l["Menu:BookStore"],icon: "fa fa-book").AddItem(new ApplicationMenuItem("BooksStore.Books",l["Menu:Books"],url: "/Books")));
运行项目,使用用户名admin
和密码1q2w3E*
登录到应用程序,然后查看新菜单项已添加到主菜单中:
当您单击到Book Store
父项下的Books
菜单项时,您将被重定向到新的空白Books Page
。
图书列表
我们将使用 jQuery库显示图书列表。数据表库通过AJAX完全起作用,它快速、流行并且提供了良好的用户体验。
Datatables库是在启动模板中配置的,因此您可以在任何页面中直接使用它,而无需在页面中包含任何样式或脚本文件。
Index.cshtml
更改Pages/Books/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><h2>@L["Books"]</h2></abp-card-header><abp-card-body><abp-table striped-rows="true" id="BooksTable"></abp-table></abp-card-body></abp-card>
abp-script
标记帮助程序用于将外部脚本添加到页面。与标准script
标记相比,它具有许多其他功能。它处理缩小和版本控制。有关详细信息,请参见捆绑和缩小文档。
abp-card
是Twitter Bootstrap的卡组件的标签帮助器。ABP框架还提供了其他有用的标签帮助程序,可以轻松使用大多数 bootstrap组件。您可以使用常规HTML
标记代替这些标记帮助器,但是使用标记帮助器可以减少HTML
代码并通过智能感知和编译时类型检查来防止错误。更多信息,请参见标签帮助器文档。
Index.js
在文件Pages/Books
夹下创建一个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);}}]}));});
abp.localization.getResource
获取一个函数,该函数用于使用服务器端定义的相同JSON
文件对文本进行本地化。这样,您可以与客户端共享本地化值。
abp.libs.datatables.normalizeConfiguration
是ABP框架定义的辅助功能。不需要使用它,但是它通过为丢失的选项提供常规默认值来简化Datatables配置。
abp.libs.datatables.createAjax
是另一个帮助程序功能,用于使ABP的动态JavaScript API代理适应Datatable的预期参数格式。
acme.bookStore.books.book.getList
是之前介绍的动态JavaScript代理功能。
luxon库也是解决方案中预先配置的标准库,因此您可以轻松地执行日期/时间操作。
有关所有配置选项,请参见数据表文档。
运行最终应用程序
您可以运行该应用程序!这部分的最终用户界面如下所示:
这是一个完全正常工作的服务器端分页,已排序和本地化的书籍表。
下一部分
请参阅本教程的下一部分。