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板塊的增刪改查展現層完成,在此記錄,方便讀者參考學習!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM