• ABP框架入门学习(三) ——UI展现层增删改查实现


    上一篇文章咋们说道新增的BOOK模块,从实体到领域层再到应用层,自动生成出来的swagger也完成,接下来咋们直接使用上面所封装的给展现层的函数

    前言准备工作:

    1、首先运行项目,在开发者模式(浏览器F12)测试getList和Create功能

    • testApp.bookStore.booksBookAppService转换为camelCase的命名空间。
    • bookBookAppService(删除AppService后缀并转换为驼峰式)的常规名称。
    • getListGetListAsync是在基类中定义的方法的常规名称CrudAppService(删除了Async后缀并转换为 camelCase)。
    • {}参数用于向方法发送一个空对象,该GetListAsync方法通常需要一个类型的对象,该对象PagedAndSortedResultRequestDto用于向服务器发送分页和排序选项(所有属性都是可选的,具有默认值,因此您可以发送一个空对象)。
    • getList函数返回一个promise您可以将回调传递给then(or done) 函数以获取从服务器返回的结果。

    检查Books数据库中的表以查看新书行。您可以尝试getupdatedelete自己运行。

    2、文本本地化

    由于我们后面会使用了很多本地化文本,所以需要将它们添加到本地化文件(en.json在项目TestApp.BookStore.Domain.Shared下Localization/BookStore文件夹下),如:@L["Books"]、@L["NewBook"]等

     脚本如下:

    {
      "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 可以在某些适当的情况下自动本地化枚举。

     

    ——————————UI项目增删改查功能开始——————————

    一、创建图书页面(查)

     在项目TestApp.BookStore.Web创建一个文件夹Books通过右键单击 Books 文件夹然后选择Add > Razor Page菜单项来添加新的 Razor Page。将其命名为Index

    添加书籍主菜单

    打开项目TestApp.BookStore.Web项目下Menus文件夹中的BookStoreMenuContributor类,在方法末尾添加如下代码:

     

    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*,您可以看到新的菜单项已添加到主菜单中:

     实现书籍列表功能

    修改Index.cshtml页面

    @page
    @using TestApp.BookStore.Localization
    @using TestApp.BookStore.Web.Pages.Books
    @using Microsoft.Extensions.Localization
    @model TestApp.BookStore.Web.Pages.Books.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 代码并通过 IntelliSense 的帮助防止错误并编译时间类型检查。有关更多信息,请查看标签助手文档。

    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(testapp.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库也是解决方案中预配置的标准库,因此您可以使用它轻松执行日期/时间操作。

    运行项目,可以查看书籍列表,如下图:

    二、创建新书(增)

    书店创建对话框

    创建模态表单

    在项目TestApp.BookStore.Web下Pages/Books的文件夹下创建一个名CreateModal.cshtml的Razor页面

    CreateModal.cshtml

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

    @page
    @using TestApp.BookStore.Localization
    @using TestApp.BookStore.Web.Pages.Books
    @using Microsoft.Extensions.Localization
    @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
    @model TestApp.BookStore.Web.Pages.Books.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 标签助手自动从CreateUpdateBookDto模型类创建表单。
    • abp-model属性指示模型对象,Book在这种情况下它是属性。
    • abp-form-content标签助手是呈现表单控件的占位符(它是可选的,仅当您在abp-dynamic-form标签中添加了一些其他内容时才需要,就像在这个页面中一样)。

    提示:Layout应该null和本例中一样,因为我们不想在通过 AJAX 加载模态框时包含所有布局。

    CreateModal.cshtml.cs

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

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.RazorPages;
    using System.Threading.Tasks;
    using TestApp.BookStore.Books;
    using Microsoft.AspNetCore.Mvc;
    
    namespace TestApp.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并添加了一些可以在页面模型类中共享的通用属性和方法。
    • [BindProperty]属性上的Book属性将发布请求数据绑定到此属性。
    • 此类只是IBookAppService在构造函数中注入 并调用处理程序CreateAsync中的方法OnPostAsync
    • 它在方法中创建一个新CreateUpdateBookDto对象OnGetASP.NET Core 可以在不创建这样的新实例的情况下工作。但是,它不会为您创建实例,并且如果您的类在类构造函数中有一些默认值分配或代码执行,它们将不起作用。CreateUpdateBookDto对于这种情况,我们为某些属性设置了默认值。

     

    调整Index页面,添加“新书”按钮

    脚本如下:

    @page
    @using TestApp.BookStore.Localization
    @using TestApp.BookStore.Web.Pages.Books
    @using Microsoft.Extensions.Localization
    @model TestApp.BookStore.Web.Pages.Books.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的新按钮:

    实现新增按钮事件,修改Pages/Books/Index.js文件

    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(testApp.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();
        });
    })

    运行应用程序并使用新的模态表单添加一些新书。

    三、更新书籍信息(改)

    在项目TestApp.BookStore.Web下Pages/Books的文件夹下创建一个名EditModal.cshtml的Razor页面

    EditModal.cshtml

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

    此页面与 非常相似CreateModal.cshtml,除了:

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

    EditModal.cshtml.cs

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

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.RazorPages;
    using System;
    using System.Threading.Tasks;
    using TestApp.BookStore.Books;
    
    namespace TestApp.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方法中,我们BookDto从 中获取 ,BookAppService并将其映射到 DTO 对象CreateUpdateBookDto
    • 用于更新实体OnPostAsync用途。BookAppService.UpdateAsync(...)

    从 BookDto 映射到 CreateUpdateBookDto

    为了能够映射BookDtoCreateUpdateBookDto,配置一个新的映射。为此,请TestApp.BookStore.Web项目中打开文件BookStoreWebAutoMapperProfile.cs并进行更改,如下所示:

    using AutoMapper;
    using TestApp.BookStore.Books;
    
    namespace TestApp.BookStore.Web;
    
    public class BookStoreWebAutoMapperProfile : Profile
    {
        public BookStoreWebAutoMapperProfile()
        {
            //Define your AutoMapper configuration here for the Web project.
            CreateMap<BookDto, CreateUpdateBookDto>();
        }
    }

    请注意,我们将在 web 层中进行映射定义作为最佳实践,因为它仅在该层中需要。

     

    将“操作”下拉列表添加到表中

    打开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(testApp.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);
                        }
                    }
                ]
            })
        );
    
        //新增操作
        var createModal = new abp.ModalManager(abp.appPath + 'Books/CreateModal');
    
        createModal.onResult(function () {
            dataTable.ajax.reload();
        });
    
        $('#NewBookButton').click(function (e) {
            e.preventDefault();
            createModal.open();
        });
    })
    • 添加了一个新ModalManager名称editModal以打开编辑模式对话框。
    • 在该部分的开头添加了一个新列columnDefs此列用于“操作”下拉按钮。
    • 编辑”操作只是调用editModal.open()打开编辑对话框。
    • editModal.onResult(...)您关闭编辑模式时,回调会刷新数据表。

    运行应用程序并编辑修改书籍信息

    最终的 UI 如下所示:

    四、删除书籍(删)

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

    {
                                        text: l('Delete'),
                                        confirmMessage: function (data) {
                                            return l(
                                                'BookDeletionConfirmationMessage',
                                                data.record.name
                                            );
                                        },
                                        action: function (data) {
                                            testApp.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),您需要将它们添加到本地化文件(en.json在项目TestApp.BookStore.Domain.Shared下Localization/BookStore文件夹下):

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

    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",
        "BookDeletionConfirmationMessage": "Are you sure to delete the book '{0}'?",
        "SuccessfullyDeleted": "Successfully deleted!"
      }
    }

    最终Page/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(testApp.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) {
                                            testApp.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);
                        }
                    }
                ]
            })
        );
    
        //新增操作
        var createModal = new abp.ModalManager(abp.appPath + 'Books/CreateModal');
    
        createModal.onResult(function () {
            dataTable.ajax.reload();
        });
    
        $('#NewBookButton').click(function (e) {
            e.preventDefault();
            createModal.open();
        });
    
        //修改操作(columnDefs新增Actions操作列)
        var editModal = new abp.ModalManager(abp.appPath + 'Books/EditModal');
        editModal.onResult(function () {
            dataTable.ajax.reload();
        });
    
    })

    运行应用程序并尝试删除书籍信息。

     

     至此,Book板块的增删改查展现层完成,在此记录,方便读者参考学习!

  • 相关阅读:
    asp.net常用的javascript经典例子
    Silverlight学习之——布局系统
    TreeView数据绑定方法
    软件测试:单元测试的一些疑问
    Silverlight学习之——Deep Zoom文件格式概述
    把生活节奏调整得慢一点
    20、Windows内核函数(1)Windows驱动开发详解笔记,字符串
    24、Windows派遣函数(2)Windows驱动开发详解笔记,直接读写方式
    21、Windows内核函数(2)Windows驱动开发详解笔记,文件操作
    27、Windows内核编程,IRP的同步(1)
  • 原文地址:https://www.cnblogs.com/become/p/15932838.html
Copyright © 2020-2023  润新知