From d075a14e43b5dad24ee3bc1916484bbd1439d017 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Tue, 3 Jul 2018 11:07:27 +0300 Subject: [PATCH] Added edit & delete actions for the books table. --- docs/Tutorials/AspNetCore-Mvc/Part-I.md | 15 +- docs/Tutorials/AspNetCore-Mvc/Part-II.md | 238 +++++++++++++++++- docs/images/bookstore-add-edit-dialog.png | Bin 0 -> 4006 bytes docs/images/bookstore-books-table-actions.png | Bin 0 -> 9339 bytes ...bp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj | 4 +- .../CreateUpdateBookDto.cs | 1 + .../BookStore/BookStoreResource.cs | 5 +- .../Localization/BookStore/en.json | 5 +- .../Localization/BookStore/tr.json | 5 +- .../Pages/Books/CreateModal.cshtml.cs | 3 +- .../Pages/Books/EditModal.cshtml | 18 ++ .../Pages/Books/EditModal.cshtml.cs | 35 +++ .../Pages/Books/Index.cshtml | 1 + .../Properties/launchSettings.json | 4 +- .../wwwroot/pages/books/index.js | 58 ++++- 15 files changed, 367 insertions(+), 25 deletions(-) create mode 100644 docs/images/bookstore-add-edit-dialog.png create mode 100644 docs/images/bookstore-books-table-actions.png create mode 100644 samples/BookStore/src/Acme.BookStore.Web/Pages/Books/EditModal.cshtml create mode 100644 samples/BookStore/src/Acme.BookStore.Web/Pages/Books/EditModal.cshtml.cs diff --git a/docs/Tutorials/AspNetCore-Mvc/Part-I.md b/docs/Tutorials/AspNetCore-Mvc/Part-I.md index 084d5da408..290c0a558e 100644 --- a/docs/Tutorials/AspNetCore-Mvc/Part-I.md +++ b/docs/Tutorials/AspNetCore-Mvc/Part-I.md @@ -2,7 +2,7 @@ ### About the Tutorial -In this tutorial series, you will build an application that is used to manage a list of books & their authors. **Entity Framework Core** (EF Core) will be used as the ORM provider (as it comes pre-configured with the startup template). +In this tutorial series, you will build an application that is used to manage a list of books & their authors. **Entity Framework Core** (EF Core) will be used as the ORM provider (as it comes pre-configured with the [startup template](https://abp.io/Templates)). This is the first part of the tutorial series. See all parts: @@ -174,14 +174,13 @@ namespace Acme.BookStore } ```` -* This DTO class is used to get book information from the user interface. -* Each property has a `[Display]` property which sets the label on the form for the related input. It's also integrated to the localization system. +* This DTO class is used to get book information from the user interface to create or update a book. * It defines data annotation attributes (like `[Required]`) to define validations for the properties. * Each property has a `[Display]` property which sets the label on UI forms for the related inputs. It's also integrated to the localization system. The same DTO will be used as View Model. That's why it defines that attribute. You may find incorrect to use DTOs as View Models. You could use a separated view model class, but we thought it's practical and makes the sample project less complex. #### IBookAppService -First, define an interface named `IBookAppService` for the book application service: +Define an interface named `IBookAppService` for the book application service: ````C# using System; @@ -205,7 +204,7 @@ namespace Acme.BookStore ```` * Defining interfaces for application services is not required by the framework. However, it's suggested as a good practice. -* `IAsyncCrudAppService` defines common CRUD methods: `GetAsync`, `GetListAsync`, `CreateAsync`, `UpdateAsync` and `DeleteAsync`. It's not required to extend it. Instead you could inherit from the empty `IApplicationService` interface and define your own methods. +* `IAsyncCrudAppService` defines common **CRUD** methods: `GetAsync`, `GetListAsync`, `CreateAsync`, `UpdateAsync` and `DeleteAsync`. It's not required to extend it. Instead you could inherit from the empty `IApplicationService` interface and define your own methods. * There are some variations of the `IAsyncCrudAppService` where you can use a single DTO or separated DTOs for each method. #### BookAppService @@ -250,7 +249,7 @@ The startup template is configured to run the [swagger UI](https://swagger.io/to ![bookstore-swagger](../../images/bookstore-swagger.png) -You will see some built-in service endpoints as well as the `Book` service and its REST-style service endpoints. +You will see some built-in service endpoints as well as the `Book` service and its REST-style endpoints. ### Dynamic JavaScript Proxies @@ -290,7 +289,7 @@ You should see a message in the console something like that: successfully created the book with id: f3f03580-c1aa-d6a9-072d-39e75c69f5c7 ```` -Check the `books` table in the database to see the new book row. You can also try `get`, `update` and `delete` functions. +Check the `books` table in the database to see the new book row. You can try `get`, `update` and `delete` functions too. ### Create the Books Page @@ -311,7 +310,7 @@ Open the `Index.cshtml` and change the content as shown below:

Books

```` -* This page inherits from the `BookStorePageBase` class which comes with the startup template and provides some shared properties/methods used by all pages. +* This page **inherits** from the `BookStorePageBase` class which comes with the startup template and provides some shared properties/methods used by all pages. #### Add Books Page to the Main Menu diff --git a/docs/Tutorials/AspNetCore-Mvc/Part-II.md b/docs/Tutorials/AspNetCore-Mvc/Part-II.md index e1ad9edf27..422e226d15 100644 --- a/docs/Tutorials/AspNetCore-Mvc/Part-II.md +++ b/docs/Tutorials/AspNetCore-Mvc/Part-II.md @@ -35,7 +35,7 @@ using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; namespace Acme.BookStore.Pages.Books { - public class CreateModalModel : AbpPageModel + public class CreateModalModel : BookStorePageModelBase { [BindProperty] public CreateUpdateBookDto Book { get; set; } @@ -56,6 +56,7 @@ namespace Acme.BookStore.Pages.Books } ```` +* This class is derived from the `BookStorePageModelBase` instead of standard `PageModel`. `BookStorePageModelBase` inherits the `PageModel` and adds some common properties/methods those can be used in your page model classes. * `[BindProperty]` attribute on the `Book` property binds post request data to this property. * This class simply injects the `IBookAppService` in its constructor and calls the `CreateAsync` method in the `OnPostAsync` handler. @@ -132,4 +133,237 @@ Now, you can **run the application** and add new books using the new modal form. ### Updating An Existing Book -TODO... \ No newline at end of file +Create a new razor page, named `EditModal.cshtml` under the `Pages/Books` folder of the `Acme.BookStore.Web` project: + +![bookstore-add-edit-dialog](../../images/bookstore-add-edit-dialog.png) + +#### EditModal.cshtml.cs + +Open the `EditModal.cshtml.cs` file (`EditModalModel` class) and replace with the following code: + +````C# +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace Acme.BookStore.Pages.Books +{ + public class EditModalModel : BookStorePageModelBase + { + [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); + } + + public async Task OnPostAsync() + { + await _bookAppService.UpdateAsync(Id, Book); + return NoContent(); + } + } +} +```` + +* `HiddenInput` and `BindProperty` are standard ASP.NET Core MVC attributes. Used `SupportsGet` to be able to get Id value from query string parameter of the request. +* Mapped `BookDto` (received from the `BookAppService.GetAsync`) to `CreateUpdateBookDto` in the `GetAsync` method. +* The `OnPostAsync` simply uses `BookAppService.UpdateAsync` to update the entity. + +#### CreateUpdateBookDto + +In order to perform `BookDto` to `CreateUpdateBookDto` object mapping, change the `CreateUpdateBookDto` class as shown below: + +````C# +using System; +using System.ComponentModel.DataAnnotations; +using Volo.Abp.AutoMapper; + +namespace Acme.BookStore +{ + [AutoMapTo(typeof(Book))] + [AutoMapFrom(typeof(BookDto))] + public class CreateUpdateBookDto + { + [Required] + [StringLength(128)] + [Display(Name = "Name")] + public string Name { get; set; } + + [Display(Name = "Type")] + public BookType Type { get; set; } = BookType.Undefined; + + [Display(Name = "PublishDate")] + public DateTime PublishDate { get; set; } + + [Display(Name = "Price")] + public float Price { get; set; } + } +} +```` + +* Just added the `[AutoMapFrom(typeof(BookDto))]` attribute to create the mapping. + +#### EditModal.cshtml + +Replace `EditModal.cshtml` content with the following content: + +````html +@page +@inherits Acme.BookStore.Pages.BookStorePageBase +@using Acme.BookStore.Pages.Books +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal +@model EditModalModel +@{ + Layout = null; +} + + + + + + + + + + +```` + +This page is very similar to the `CreateModal.cshtml` except; + +* It includes an `abp-input` for the `Id` property to store id of the editing book. +* It uses `Books/EditModal` as the post URL and *Update* text as the modal header. + +#### Add "Actions" Dropdown to the Table + +We will add a dropdown button ("Actions") for each row of the table. The final UI looks like this: + +![bookstore-books-table-actions](../../images/bookstore-books-table-actions.png) + +Open the `Pages/Books/Index.cshtml` page and change the table section as shown below: + +````html + + + + @L["Actions"] + @L["Name"] + @L["Type"] + @L["PublishDate"] + @L["Price"] + @L["CreationTime"] + + + +```` + +* Just added a new `th` tag for the "Actions" button. + +Open the `wwwroot/pages/books/index.js` and replace the content as below: + +````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({ + order: [[1, "asc"]], + ajax: abp.libs.datatables.createAjax(acme.bookStore.book.getList), + columnDefs: [ + { + targets: 0, + data: null, + orderable: false, + autoWidth: false, + defaultContent: '', + rowAction: { + text: ' ' + l('Actions') + + ' ', + items: + [ + { + text: l('Edit'), + visible: function () { return true; }, + action: function (data) { + editModal.open({ + id: data.record.id + }); + } + }, + { + text: l('Delete'), + visible: function () { return true; }, + confirmMessage: function (data) { + return l('BookDeletionConfirmationMessage', + data.record.name); + }, + action: function (data) { + acme.bookStore.book + .delete(data.record.id) + .then(function () { + abp.notify.info(l('SuccessfullyDeleted')); + dataTable.ajax.reload(); + }); + } + } + ] + } + }, + { + targets: 1, + data: "name" + }, + { + targets: 2, + data: "type" + }, + { + targets: 3, + data: "publishDate" + }, + { + targets: 4, + data: "price" + }, + { + targets: 5, + data: "creationTime" + } + ] + }); + + createModal.onResult(function () { + dataTable.ajax.reload(); + }); + + editModal.onResult(function () { + dataTable.ajax.reload(); + }); + + $('#NewBookButton').click(function (e) { + e.preventDefault(); + createModal.open(); + }); +}); +```` + +* Added a new `ModalManager` named `editModal` to open the edit modal dialog. +* Added a new column at the beginning of the `columnDefs` section. This column is used for the "Actions" dropdown button. +* "Edit" action simply calls `editModal.open` to open the edit dialog. +* Also added a "Delete" button to delete the book. \ No newline at end of file diff --git a/docs/images/bookstore-add-edit-dialog.png b/docs/images/bookstore-add-edit-dialog.png new file mode 100644 index 0000000000000000000000000000000000000000..b28d8736dd19efa21f65e6e5a9c8d4b82a613f4c GIT binary patch literal 4006 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 M07*qoM6N<$g0meFxBvhE literal 0 HcmV?d00001 diff --git a/docs/images/bookstore-books-table-actions.png b/docs/images/bookstore-books-table-actions.png new file mode 100644 index 0000000000000000000000000000000000000000..bc1d4e3f6f06b09879bff2ecdedbd0ddd56f0d0b GIT binary patch literal 9339 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 - - + + diff --git a/samples/BookStore/src/Acme.BookStore.Application/CreateUpdateBookDto.cs b/samples/BookStore/src/Acme.BookStore.Application/CreateUpdateBookDto.cs index 018b67bdbf..a74a786682 100644 --- a/samples/BookStore/src/Acme.BookStore.Application/CreateUpdateBookDto.cs +++ b/samples/BookStore/src/Acme.BookStore.Application/CreateUpdateBookDto.cs @@ -5,6 +5,7 @@ using Volo.Abp.AutoMapper; namespace Acme.BookStore { [AutoMapTo(typeof(Book))] + [AutoMapFrom(typeof(BookDto))] public class CreateUpdateBookDto { [Required] diff --git a/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/BookStoreResource.cs b/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/BookStoreResource.cs index 19585fdf72..61a4d30826 100644 --- a/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/BookStoreResource.cs +++ b/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/BookStoreResource.cs @@ -1,5 +1,8 @@ -namespace Acme.BookStore.Localization.BookStore +using Volo.Abp.Localization; + +namespace Acme.BookStore.Localization.BookStore { + [LocalizationResourceName("BookStore")] public class BookStoreResource { diff --git a/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/en.json b/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/en.json index 3011e0dd3a..514f291cdb 100644 --- a/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/en.json +++ b/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/en.json @@ -12,6 +12,9 @@ "Price": "Price", "CreationTime": "Creation Time", "NewBook": "New book", - "Books": "Books" + "Books": "Books", + "Update": "Update", + "BookDeletionConfirmationMessage": "Are you sure to delete this book: {0}", + "SuccessfullyDeleted": "Successfully deleted." } } \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/tr.json b/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/tr.json index c99d490575..c367878efd 100644 --- a/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/tr.json +++ b/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/tr.json @@ -12,6 +12,9 @@ "Price": "Fiyat", "CreationTime": "Oluşturulma zamanı", "NewBook": "Yeni kitap", - "Books": "Kitaplar" + "Books": "Kitaplar", + "Update": "Güncelle", + "BookDeletionConfirmationMessage": "Bu kitabı silmek istediğinize emin misiniz: {0}", + "SuccessfullyDeleted": "Başarıyla silindi." } } \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/CreateModal.cshtml.cs b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/CreateModal.cshtml.cs index 0e9ed1ba72..1dc5f368fe 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/CreateModal.cshtml.cs +++ b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/CreateModal.cshtml.cs @@ -1,10 +1,9 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; namespace Acme.BookStore.Pages.Books { - public class CreateModalModel : AbpPageModel + public class CreateModalModel : BookStorePageModelBase { [BindProperty] public CreateUpdateBookDto Book { get; set; } diff --git a/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/EditModal.cshtml b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/EditModal.cshtml new file mode 100644 index 0000000000..fef54ae3fb --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/EditModal.cshtml @@ -0,0 +1,18 @@ +@page +@inherits Acme.BookStore.Pages.BookStorePageBase +@using Acme.BookStore.Pages.Books +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal +@model EditModalModel +@{ + Layout = null; +} + + + + + + + + + + \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/EditModal.cshtml.cs b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/EditModal.cshtml.cs new file mode 100644 index 0000000000..c419fe01c1 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/EditModal.cshtml.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace Acme.BookStore.Pages.Books +{ + public class EditModalModel : BookStorePageModelBase + { + [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); + } + + public async Task OnPostAsync() + { + await _bookAppService.UpdateAsync(Id, Book); + return NoContent(); + } + } +} \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/Index.cshtml b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/Index.cshtml index 6233c32c8d..41edd032e8 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/Index.cshtml +++ b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/Index.cshtml @@ -24,6 +24,7 @@ + @L["Actions"] @L["Name"] @L["Type"] @L["PublishDate"] diff --git a/samples/BookStore/src/Acme.BookStore.Web/Properties/launchSettings.json b/samples/BookStore/src/Acme.BookStore.Web/Properties/launchSettings.json index 6913413ef0..79d4dca46f 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Properties/launchSettings.json +++ b/samples/BookStore/src/Acme.BookStore.Web/Properties/launchSettings.json @@ -3,7 +3,7 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:53929/", + "applicationUrl": "http://localhost:54929/", "sslPort": 0 } }, @@ -21,7 +21,7 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, - "applicationUrl": "http://localhost:53932/" + "applicationUrl": "http://localhost:54932/" } } } \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Web/wwwroot/pages/books/index.js b/samples/BookStore/src/Acme.BookStore.Web/wwwroot/pages/books/index.js index 160c6327d9..6f8884f262 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/wwwroot/pages/books/index.js +++ b/samples/BookStore/src/Acme.BookStore.Web/wwwroot/pages/books/index.js @@ -1,36 +1,82 @@ $(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({ + order: [[1, "asc"]], ajax: abp.libs.datatables.createAjax(acme.bookStore.book.getList), columnDefs: [ { targets: 0, - data: "name" + data: null, + orderable: false, + autoWidth: false, + defaultContent: '', + rowAction: { + text: ' ' + l('Actions') + ' ', + items: + [ + { + text: l('Edit'), + visible: function () { return true; }, + action: function (data) { + editModal.open({ + id: data.record.id + }); + } + }, + { + text: l('Delete'), + visible: function () { return true; }, + confirmMessage: function (data) { + return l('BookDeletionConfirmationMessage', data.record.name); + }, + action: function (data) { + acme.bookStore.book + .delete(data.record.id) + .then(function () { + abp.notify.info(l('SuccessfullyDeleted')); + dataTable.ajax.reload(); + }); + } + } + ] + } }, { targets: 1, - data: "type" + data: "name" }, { targets: 2, - data: "publishDate" + data: "type" }, { targets: 3, - data: "price" + data: "publishDate" }, { targets: 4, + data: "price" + }, + { + targets: 5, data: "creationTime" } ] }); - var createModal = new abp.ModalManager(abp.appPath + 'Books/CreateModal'); - createModal.onResult(function () { dataTable.ajax.reload(); }); + editModal.onResult(function () { + dataTable.ajax.reload(); + }); + $('#NewBookButton').click(function (e) { e.preventDefault(); createModal.open();