* This class is derived from the `BookStorePageModel` instead of standard `PageModel`. `BookStorePageModel` indirectly inherits the `PageModel` and adds some common properties & methods that can be shared in your page model classes.
* This class is derived from the `BookStorePageModel` instead of the standard `PageModel`. `BookStorePageModel` indirectly inherits the `PageModel` and adds some common properties & methods that can be shared 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 the constructor and calls the `CreateAsync` method in the `OnPostAsync` handler.
* It creates a new `CreateUpdateBookDto` object in the `OnGet` method. ASP.NET Core can work without creating a new instance like that. However, it doesn't create an instance for you and if your class has some default value assignments or code execution in the class constructor, they won't work. For this case, we set default values for some of the `CreateUpdateBookDto` properties.
@ -129,7 +129,7 @@ Open the `CreateModal.cshtml` file and paste the code below:
</abp-dynamic-form>
````
* This modal uses `abp-dynamic-form` [tag helper](../UI/AspNetCore/Tag-Helpers/Dynamic-Forms.md) to automatically create the form from the `CreateBookViewModel` model class.
* This modal uses `abp-dynamic-form` [tag helper](../UI/AspNetCore/Tag-Helpers/Dynamic-Forms.md) to automatically create the form from the `CreateBookViewModel` model class.
* `abp-model` attribute indicates the model object where it's the `Book` property in this case.
* `abp-form-content` tag helper is a placeholder to render the form controls (it is optional and needed only if you have added some other content in the `abp-dynamic-form` tag, just like in this page).
@ -155,7 +155,7 @@ Open the `Pages/Books/Index.cshtml` and set the content of `abp-card-header` tag
</abp-card-header>
````
The final content of the `Index.cshtml` is shown below:
The final content of `Index.cshtml` is shown below:
````html
@page
@ -193,7 +193,7 @@ This adds a new button called **New book** to the **top-right** of the table:
* `abp.ModalManager` is a helper class to manage modals in the client side. It internally uses Twitter Bootstrap's standard modal, but abstracts many details by providing a simple API.
* `abp.ModalManager` is a helper class to manage modals on the client side. It internally uses Twitter Bootstrap's standard modal, but abstracts many details by providing a simple API.
* `createModal.onResult(...)` used to refresh the data table after creating a new book.
* `createModal.open();` is used to open the model to create a new book.
The final content of the `Index.js`should be like that:
The final content of the `Index.js`file should be like this:
````js
$(function () {
@ -290,7 +290,7 @@ Create a new razor page, named `EditModal.cshtml` under the `Pages/Books` folder
### EditModal.cshtml.cs
Open the `EditModal.cshtml.cs` file (`EditModalModel` class) and replace with the following code:
Open the `EditModal.cshtml.cs` file (`EditModalModel` class) and replace it with the following code:
* `[HiddenInput]` and `[BindProperty]` are standard ASP.NET Core MVC attributes. `SupportsGet` is used to be able to get `Id` value from query string parameter of the request.
* In the `OnGetAsync` method, we get `BookDto` from the `BookAppService` and this is being mapped to the DTO object `CreateUpdateBookDto`.
* `[HiddenInput]` and `[BindProperty]` are standard ASP.NET Core MVC attributes. `SupportsGet` is used to be able to get the `Id` value from the query string parameter of the request.
* In the `OnGetAsync` method, we get the `BookDto` from the `BookAppService` and this is being mapped to the DTO object `CreateUpdateBookDto`.
* The `OnPostAsync` uses `BookAppService.UpdateAsync(...)` to update the entity.
### Mapping from BookDto to CreateUpdateBookDto
To be able to map the `BookDto` to `CreateUpdateBookDto`, configure a new mapping. To do this, open the `BookStoreWebAutoMapperProfile.cs` in the `Acme.BookStore.Web` project and change it as shown below:
To be able to map the `BookDto` to `CreateUpdateBookDto`, configure a new mapping. To do this, open the `BookStoreWebAutoMapperProfile.cs`file in the `Acme.BookStore.Web` project and change it as shown below:
````csharp
using AutoMapper;
@ -385,16 +385,16 @@ Replace `EditModal.cshtml` content with the following content:
</abp-dynamic-form>
````
This page is very similar to the `CreateModal.cshtml`, except:
This page is very similar to `CreateModal.cshtml`, except:
* It includes an `abp-input` for the `Id` property to store `Id` of the editing book (which is a hidden input).
* It includes an `abp-input` for the `Id` property to store the `Id` of the editing book (which is a hidden input).
* It uses `Books/EditModal` as the post URL.
### Add "Actions" Dropdown to the Table
We will add a dropdown button to the table named *Actions*.
Open the `Pages/Books/Index.js` and replace the content as below:
Open the `Pages/Books/Index.js`file and replace the content as below:
````js
$(function () {
@ -482,8 +482,8 @@ $(function () {
* 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.
* `editModal.onResult(...)` callback refreshes the data table when you close the edit modal.
* The "*Edit*" action simply calls `editModal.open()` to open the edit dialog.
* The `editModal.onResult(...)` callback refreshes the data table when you close the edit modal.
You can run the application and edit any book by selecting the edit action on a book.
@ -493,7 +493,7 @@ The final UI looks as below:
## Deleting a Book
Open the `Pages/Books/Index.js` and add a new item to the `rowAction``items`:
Open the `Pages/Books/Index.js`file and add a new item to the `rowAction``items`:
````js
{
@ -512,8 +512,8 @@ Open the `Pages/Books/Index.js` and add a new item to the `rowAction` `items`:
}
````
* `confirmMessage` option is used to ask a confirmation question before executing the `action`.
* `acme.bookStore.books.book.delete(...)` method makes an AJAX request to the server to delete a book.
* The `confirmMessage` option is used to ask a confirmation question before executing the `action`.
* The `acme.bookStore.books.book.delete(...)` method makes an AJAX request to the server to delete a book.
* `abp.notify.info()` shows a notification after the delete operation.
Since we've used two new localization texts (`BookDeletionConfirmationMessage` and `SuccessfullyDeleted`) you need to add these to the localization file (`en.json` under the `Localization/BookStore` folder of the `Acme.BookStore.Domain.Shared` project):
@ -721,10 +721,10 @@ Open `/src/app/book/book.component.html` and make the following changes:
</abp-modal>
```
* Added `New book` button to the card header..
* Added a `New book` button to the card header..
* Added the `abp-modal` which renders a modal to allow user to create a new book. `abp-modal` is a pre-built component to show modals. While you could use another approach to show a modal, `abp-modal` provides additional benefits.
You can open your browser and click **New book** button to see the new modal.
You can open your browser and click the **New book** button to see the new modal.

@ -801,11 +801,11 @@ export class BookComponent implements OnInit {
```
* Imported `FormGroup`, `FormBuilder` and `Validators` from `@angular/forms`.
* Added `form: FormGroup` property.
* Added `bookTypes` property as a list of `BookType` enum members. That will be used in form options.
* Added a `form: FormGroup` property.
* Added a `bookTypes` property as a list of `BookType` enum members. That will be used in form options.
* Injected `FormBuilder` into the constructor. [FormBuilder](https://angular.io/api/forms/FormBuilder) provides convenient methods for generating form controls. It reduces the amount of boilerplate needed to build complex forms.
* Added `buildForm` method to the end of the file and executed the `buildForm()` in the `createBook` method.
* Added `save` method.
* Added a `buildForm` method to the end of the file and executed the `buildForm()` in the `createBook` method.
* Added a `save` method.
Open `/src/app/book/book.component.html` and replace `<ng-template #abpBody> </ng-template>` with the following code part:
@ -863,7 +863,7 @@ Also replace `<ng-template #abpFooter> </ng-template>` with the following code p
### Datepicker
We've used [NgBootstrap datepicker](https://ng-bootstrap.github.io/#/components/datepicker/overview) in this component. So, need to arrange dependencies related to this component.
We've used [NgBootstrap datepicker](https://ng-bootstrap.github.io/#/components/datepicker/overview) in this component. So, we need to arrange the dependencies related to this component.
Open `/src/app/book/book.module.ts` and replace the content as below:
@ -959,7 +959,7 @@ export class BookComponent implements OnInit {
```
* Imported ` NgbDateNativeAdapter` and `NgbDateAdapter`.
* We added a new provider `NgbDateAdapter` that converts Datepicker value to `Date` type. See the [datepicker adapters](https://ng-bootstrap.github.io/#/components/datepicker/overview) for more details.
* We added a new provider `NgbDateAdapter` that converts the Datepicker value to `Date` type. Check out the [datepicker adapters](https://ng-bootstrap.github.io/#/components/datepicker/overview) for more details.
Now, you can open your browser to see the changes:
@ -1054,14 +1054,14 @@ export class BookComponent implements OnInit {
```
* We declared a variable named `selectedBook` as `BookDto`.
* We added `editBook` method. This method fetches the book with the given `id` and sets it to `selectedBook` object.
* We added an `editBook` method. This method fetches the book with the given `id` and sets it to `selectedBook` object.
* We replaced the `buildForm` method so that it creates the form with the `selectedBook` data.
* We replaced the `createBook` method so it sets `selectedBook` to an empty object.
* We changed the `save` method to handle both of create and update operations.
### Add "Actions" Dropdown to the Table
Open the `/src/app/book/book.component.html` and add the following `ngx-datatable-column` definition as the first column in the `ngx-datatable`:
Open `/src/app/book/book.component.html` and add the following `ngx-datatable-column` definition as the first column in the `ngx-datatable`:
```html
<ngx-datatable-column
@ -1101,11 +1101,11 @@ Also, change the `ng-template #abpHeader` section as shown below:
</ng-template>
```
This template will show **Edit** text for edit record operation, **New Book** for new record operation in the title.
This template will show the **Edit** text for edit record operation, **New Book** for new record operation in the title.
## Deleting a Book
Open the `/src/app/book/book.component.ts` and inject the `ConfirmationService`.
Open the `/src/app/book/book.component.ts`file and inject the `ConfirmationService`.
Replace the constructor as below:
@ -1137,7 +1137,7 @@ delete(id: string) {
* We injected `ConfirmationService` to the constructor.
* Added a `delete` method.
> See the [Confirmation Popup documentation](../UI/Angular/Confirmation-Service) for more about this service.
> Check out the [Confirmation Popup documentation](../UI/Angular/Confirmation-Service) for more about this service.
### Add a Delete Button
@ -1169,7 +1169,7 @@ Clicking the "Delete" action calls the `delete` method which then shows a confir
In this section, you will learn how to create a new modal dialog form to create a new book. Since we've inherited from the `AbpCrudPageBase`, we only need to develop the view part.
### Add "New Button" Button
### Add a "New Button" Button
Open the `Books.razor` and replace the `<CardHeader>` section with the following code:
@ -1191,7 +1191,7 @@ This will change the card header by adding a "New book" button to the right side
Now, we can add a modal that will be opened when we click to the button.
Now, we can add a modal that will be opened when we click the button.
### Book Creation Modal
@ -1259,7 +1259,7 @@ This code requires a service; Inject the `AbpBlazorMessageLocalizerHelper<T>` at
````
* The form implements validation and the `AbpBlazorMessageLocalizerHelper` is used to simply localize the validation messages.
* `CreateModal` object, `CloseCreateModalAsync` and `CreateEntityAsync` method are defined by the base class. See the [Blazorise documentation](https://blazorise.com/docs/) if you want to understand the `Modal` and the other components.
* The `CreateModal` object, `CloseCreateModalAsync` and `CreateEntityAsync` methods are defined by the base class. Check out the [Blazorise documentation](https://blazorise.com/docs/) if you want to understand the `Modal` and the other components.
That's all. Run the application and try to add a new book:
@ -1267,7 +1267,7 @@ That's all. Run the application and try to add a new book:
## Updating a Book
Editing a books is similar to the creating a new book.
Editing a book is similar to creating a new book.
### Actions Dropdown
@ -1287,7 +1287,7 @@ Open the `Books.razor` and add the following `DataGridEntityActionsColumn` secti
* `OpenEditModalAsync` is defined in the base class which takes the entity (book) to edit.
`DataGridEntityActionsColumn` component is used to show an "Actions" dropdown for each row in the `DataGrid`. `DataGridEntityActionsColumn` shows a **single button** instead of a dropdown if there is only one available action inside it:
The `DataGridEntityActionsColumn` component is used to show an "Actions" dropdown for each row in the `DataGrid`. The`DataGridEntityActionsColumn` shows a **single button** instead of a dropdown if there is only one available action inside it: