From c4ce990f37e952cb159fff072c12bd5b13bb012d Mon Sep 17 00:00:00 2001 From: Engincan VESKE Date: Mon, 16 May 2022 09:46:08 +0300 Subject: [PATCH 01/17] Create app-nolayers documentation --- .../Application-Single-Layer.md | 68 +++++++++++++++++++ docs/en/docs-nav.json | 4 ++ 2 files changed, 72 insertions(+) create mode 100644 docs/en/Startup-Templates/Application-Single-Layer.md diff --git a/docs/en/Startup-Templates/Application-Single-Layer.md b/docs/en/Startup-Templates/Application-Single-Layer.md new file mode 100644 index 0000000000..d9674b0fd6 --- /dev/null +++ b/docs/en/Startup-Templates/Application-Single-Layer.md @@ -0,0 +1,68 @@ +# Application (Single Layer) Startup Template + +## Introduction + +This template provides a single-layered application for quick start with ABP Framework. + +This document explains the **solution structure** and project in details. If you want to start quickly, you can follow the guides below: + + + +### What is the Difference Between the Application Startup Template? + +ABP's [Application Startup Template](Application.md) provides a well-organized and layered solution to create maintainable business applications based on the [Domain Driven Design](../Domain-Driven-Design.md) (DDD) practises. However, some developers find this template little bit complex (or unneccessary) for simple and short-time applications. + +In that point, for such applications a single-layered application template is created. Time template has the same functionality, features and modules on runtime with the [Application Startup Template](Application.md) but the development model is minimal and everything is in the single project (`.csproj`). + +## How to Start with? + +You can use the [ABP CLI](../CLI.md) to create a new project using this startup template. Alternatively, you can directly create & download from the [Get Started](https://abp.io/get-started) page. How to download via CLI is explained in this section. + +First, install the ABP CLI if you haven't installed before: + +```bash +dotnet tool install -g Volo.Abp.Cli +``` + +Then, use the `abp new` command in an empty folder to create a new solution: + +```bash +abp new Acme.BookStore -t app-nolayers +``` + +* `Acme.BookStore` is the solution name, like *YourCompany.YourProduct*. You can use single level, two-levels or three-levels naming. +* This example specifies the template name (`-t` or `--template` option). + +### Specify the UI Framework + +This template provides multiple UI frameworks: + +* `mvc`: ASP.NET Core MVC UI with Razor Pages +* `blazor-server`: Blazor Server UI +* `angular`: Angular UI +* `none`: Without UI + +If you choose `Angular` as the UI framework, the solution is being separated into two folders: + +* `angular` folder contains the Angular UI application, the client-side code. +* `aspnet-core` folder contains the ASP.NET Core solution, the server-side code. \ No newline at end of file diff --git a/docs/en/images/bookstore-single-layer-solution-structure.png b/docs/en/images/bookstore-single-layer-solution-structure.png new file mode 100644 index 0000000000000000000000000000000000000000..edc495d909ac14d7560b8a83e9ddfeb038cc5941 GIT binary patch literal 23834 zcma%iby!qw_wFF2q_i~BjYvsLcXtWW-Q9v9ozmSflynIrAdPg_(A_BvaklUKo$tKA zyv`q7%m!xWna#8Ab;o+bl@+DXQHW4LAP~BYw74n=1g8&twLC`xe$tf_MGAa_b5WHN z1yzlZ>;V@D79t8FAW%&#>itJV;QEE5w2li1gwg%y3$D+h)D#5Lua*%PQTP0C)Q;|} zKArXSr173g2rdXqhBg|Gq!ZaPK$b?bz^rZGGN7^&Qg8K(9NDHk)O|7O*uF2l*shdo zIlzi|)}Cd&*lZ5h{YXiszRjqwxARNmbD^=d*F6KD>GGb_V^Qv&_>KyCY_Fv8b9m6E zWn^q#Y`Ynzxg59b2z~b5_JQmy!Fcy6=0hQ-+R48(Yz{jYpYCjS%2oX>`%ZaVRg#c%F7~pY?(hXqUsSrT%^tJo z)M+DmW6Y%9*z~-wep-!tct_|}!O&;npw~w2&olohjCSmQkr(μunD;R&@nSR~pp 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 Oq^YK>T72L7`F{bF#X9)_ literal 0 HcmV?d00001 diff --git a/docs/en/images/single-layer-folder-structure.png b/docs/en/images/single-layer-folder-structure.png new file mode 100644 index 0000000000000000000000000000000000000000..06aa77049c1238e3403a09511f2a5344f37fc35b GIT binary patch literal 4485 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 bK=HExPO~rp&hKh%L4_$S^5cB}JixyI@2h!m literal 0 HcmV?d00001 From 79e57e86d53df6a67c76f36cd1b9925aeb96ef4f Mon Sep 17 00:00:00 2001 From: Engincan VESKE Date: Mon, 16 May 2022 15:17:20 +0300 Subject: [PATCH 03/17] Update the Application-Single-Layer.md --- .../Application-Single-Layer.md | 53 ++++++++++-------- .../single-layer-angular-folder-structure.png | Bin 0 -> 16705 bytes 2 files changed, 30 insertions(+), 23 deletions(-) create mode 100644 docs/en/images/single-layer-angular-folder-structure.png diff --git a/docs/en/Startup-Templates/Application-Single-Layer.md b/docs/en/Startup-Templates/Application-Single-Layer.md index 651988dcac..245ef435c1 100644 --- a/docs/en/Startup-Templates/Application-Single-Layer.md +++ b/docs/en/Startup-Templates/Application-Single-Layer.md @@ -2,23 +2,21 @@ ## Introduction -This template provides a single-layered application for quick start with ABP Framework. +This template provides a single-layered application for a quick start with ABP Framework. -This document explains the **solution structure** and project in details. If you want to start quickly, you can follow the guides below: - - +This document explains the **solution structure** and project in detail. ### What is the Difference Between the Application Startup Template? -ABP's [Application Startup Template](Application.md) provides a well-organized and layered solution to create maintainable business applications based on the [Domain Driven Design](../Domain-Driven-Design.md) (DDD) practises. However, some developers find this template little bit complex (or unneccessary) for simple and short-time applications. +ABP's [Application Startup Template](Application.md) provides a well-organized and layered solution to create maintainable business applications based on the [Domain Driven Design](../Domain-Driven-Design.md) (DDD) practices. However, some developers find this template a little bit complex (or unnecessary) for simple and short-time applications. -In that point, for such applications a single-layered application template is created. This template has the same functionality, features and modules on runtime with the [Application Startup Template](Application.md) and ABP modules are already installed but the development model is minimal and everything is in the single project (`.csproj`). +At this point, a single-layer application template has been created for such applications. This template has the same functionality, features and modules on runtime with the [Application Startup Template](Application.md) but the development model is minimal and everything is in a single project (`.csproj`). ## How to Start with? -You can use the [ABP CLI](../CLI.md) to create a new project using this startup template. Alternatively, you can directly create & download from the [Get Started](https://abp.io/get-started) page. How to download via CLI is explained in this section. +You can use the [ABP CLI](../CLI.md) to create a new project using this startup template. Alternatively, you can directly create & download this startup template from the [Get Started](https://abp.io/get-started) page. How to download via CLI is explained in this section. -First, install the ABP CLI if you haven't installed before: +Firstly, install the ABP CLI if you haven't installed it before: ```bash dotnet tool install -g Volo.Abp.Cli @@ -30,9 +28,9 @@ Then, use the `abp new` command in an empty folder to create a new solution: abp new Acme.BookStore -t app-nolayers ``` -* `Acme.BookStore` is the solution name, like *YourCompany.YourProduct*. You can use single level, two-levels or three-levels naming. -* This example specifies the template name (`-t` or `--template` option). - +* `Acme.BookStore` is the solution name, like *YourCompany.YourProduct*. You can use single-level, two-level or three-level naming. +* In this example, the `-t` option (or `--template` option) specifies the template name. + ### Specify the UI Framework This template provides multiple UI frameworks: @@ -42,15 +40,15 @@ This template provides multiple UI frameworks: * `angular`: Angular UI * `none`: Without UI -> Currently, this template doesn't have Blazor WASM UI, because it requries 3 projects at least (server-side, UI and shared library between these two projects). We'll consider to add Blazor WASM UI support based on your feedback. +> Currently, this template doesn't have Blazor WASM UI, because it requires 3 projects at least (server-side, UI and shared library between these two projects). -Use the `-u` or `--ui` option to specify the UI framework, while creating the solution: +Use the `-u` or `--ui` option to specify the UI framework while creating the solution: ```bash abp new Acme.BookStore -t app-nolayers -u angular ``` -* This example specifies the UI type (`-u` option) as `angular`, you can also specify the `mvc` or `blazor-server` for UI type. +* This example specifies the UI type (`-u` option) as `angular`, you can also specify `mvc` or `blazor-server` for UI type. ### Specify the Database Provider @@ -62,26 +60,27 @@ This template supports the following database providers: Use the `-d` (or `--database-provider`) option to specify the database provider while creating the solution: ```bash -abp new Acme.BookStore -d mongodb +abp new Acme.BookStore -t app-nolayers -d mongodb ``` ## Solution Structure -If you don't specify any additional option while creating an `app-nolayers` template, you will have a solution like shown below: +If you don't specify any additional options while creating an `app-nolayers` template, you will have a solution like shown below: ![](../images/bookstore-single-layer-solution-structure.png) -As you can see, it's a single layer template rather than a layered-architecture solution (like `Application Startup Template`). +As you can see, it's a single-layer template rather than a layered-architecture solution (like the `Application Startup Template`). ### Folder Structure -Since this template provides single layer solution, we've seperated concerns into folders instead of layers and you can see the pre-defined folders like shown below: +Since this template provides single-layer solution, we've separated concerns into folders instead of layers and you can see the pre-defined folders like shown below: ![](../images/single-layer-folder-structure.png) -* You can define your `entities`, `application services`, `DTOs` etc. in this single project (in the related folders). +* You can define your `entities`, `application services`, `DTOs`, etc. in this single project (in the related folders). +* For example, you can define your `application services` and `DTOs` under the **Services** folder. -## How to Run? +### How to Run? Before running the application, you need to create the database and seed the initial data. To do that, you can run the following command in the directory of your project: @@ -89,13 +88,21 @@ Before running the application, you need to create the database and seed the ini dotnet run --migrate-database ``` -* This command will create the database and seed the initial data for you. Then you can run the application with any IDE that supports .NET or by running the `dotnet run` CLI command in the directory of your project. Default username is `admin` and password is `1q2w3E*`. +* This command will create the database and seed the initial data for you. Then you can run the application with any IDE that supports .NET or by running the `dotnet run` CLI command in the directory of your project. The default username is `admin` and the password is `1q2w3E*`. + +> While creating a database & applying migrations seem only necessary for relational databases, this project comes even if you choose a NoSQL database provider (like MongoDB). In that case, it still seeds the initial data which is necessary for the application. > If you create an `app-nolayers` template with **Angular UI** you also need to run the angular project to see the UI. -### Angular UI +### Angular UI If you choose `Angular` as the UI framework, the solution is being separated into two folders: * `angular` folder contains the Angular UI application, the client-side code. -* `aspnet-core` folder contains the ASP.NET Core solution, the server-side code. \ No newline at end of file +* `aspnet-core` folder contains the ASP.NET Core solution (a single project), the server-side code. + +The server-side is similar to the solution described in the [Solution Structure](#solution-structure) section above. This project serves the API, so the `Angular` application can consume it. + +Angular application folder structure looks like below: + +![](../images/single-layer-angular-folder-structure.png) \ No newline at end of file diff --git a/docs/en/images/single-layer-angular-folder-structure.png b/docs/en/images/single-layer-angular-folder-structure.png new file mode 100644 index 0000000000000000000000000000000000000000..91fe09d61e2af7db8fd2a9e79247f349314c6041 GIT binary patch literal 16705 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 Date: Tue, 17 May 2022 10:51:31 +0300 Subject: [PATCH 04/17] Update Application-Single-Layer.md --- docs/en/Startup-Templates/Application-Single-Layer.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/en/Startup-Templates/Application-Single-Layer.md b/docs/en/Startup-Templates/Application-Single-Layer.md index 245ef435c1..81f9e169df 100644 --- a/docs/en/Startup-Templates/Application-Single-Layer.md +++ b/docs/en/Startup-Templates/Application-Single-Layer.md @@ -69,7 +69,7 @@ If you don't specify any additional options while creating an `app-nolayers` tem ![](../images/bookstore-single-layer-solution-structure.png) -As you can see, it's a single-layer template rather than a layered-architecture solution (like the `Application Startup Template`). +It's a single-layer template rather than a layered-architecture solution (like the `Application Startup Template`). It's helpful to create a running application quickly without considering the layered architecture. ### Folder Structure @@ -92,8 +92,6 @@ dotnet run --migrate-database > While creating a database & applying migrations seem only necessary for relational databases, this project comes even if you choose a NoSQL database provider (like MongoDB). In that case, it still seeds the initial data which is necessary for the application. -> If you create an `app-nolayers` template with **Angular UI** you also need to run the angular project to see the UI. - ### Angular UI If you choose `Angular` as the UI framework, the solution is being separated into two folders: @@ -103,6 +101,6 @@ If you choose `Angular` as the UI framework, the solution is being separated int The server-side is similar to the solution described in the [Solution Structure](#solution-structure) section above. This project serves the API, so the `Angular` application can consume it. -Angular application folder structure looks like below: +The client-side application consumes the HTTP APIs as mentioned. You can see the folder structure of the Angular project shown below: -![](../images/single-layer-angular-folder-structure.png) \ No newline at end of file +![](../images/single-layer-angular-folder-structure.png) From e3eea317af29fd0811ae251e45cc20369c7dce33 Mon Sep 17 00:00:00 2001 From: Engincan VESKE Date: Tue, 17 May 2022 13:41:10 +0300 Subject: [PATCH 05/17] Create Quick Start Documentation for `app-nolayers` template --- docs/en/Tutorials/Todo/Single-Layer/Index.md | 347 +++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100644 docs/en/Tutorials/Todo/Single-Layer/Index.md diff --git a/docs/en/Tutorials/Todo/Single-Layer/Index.md b/docs/en/Tutorials/Todo/Single-Layer/Index.md new file mode 100644 index 0000000000..fe70013e19 --- /dev/null +++ b/docs/en/Tutorials/Todo/Single-Layer/Index.md @@ -0,0 +1,347 @@ +# Quick Start + +````json +//[doc-params] +{ + "UI": ["MVC", "BlazorServer", "NG"], + "DB": ["EF", "Mongo"] +} +```` + +This is a single-part quick-start tutorial to build a simple todo application with the ABP Framework. Here's a screenshot from the final application: + +![todo-list](../todo-list.png) + +You can find the source code of the completed application [here](TODO: sample's url???). + +## Pre-Requirements + +* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 6.0+](https://dotnet.microsoft.com/download/dotnet) development. + +{{if DB=="Mongo"}} + +* [MongoDB Server 4.0+](https://docs.mongodb.com/manual/administration/install-community/) + +{{end}} + +{{if UI=="NG"}} + +* [Node v14.x](https://nodejs.org/) + +{{end}} + +## Creating a New Solution + +In this tutorial, we will use the [ABP CLI](../../CLI.md) to create the sample application with the ABP Framework. You can run the following command in a command-line terminal to install the **ABP CLI**, if you haven't installed it yet: + +````bash +dotnet tool install -g Volo.Abp.Cli +```` + +Then create an empty folder, open a command-line terminal and execute the following command in the terminal: + +````bash +abp new TodoApp -t app-nolayers{{if UI=="BlazorServer"}} -u blazor-server{{else if UI=="NG"}} -u angular{{end}}{{if DB=="Mongo"}} -d mongodb{{end}} +```` + +{{if UI=="NG"}} + +This will create a new solution with a single-project, named *TodoApp* with `angular` and `aspnet-core` folders. Once the solution is ready, open the solution in your favorite IDE. + +{{else}} + +This will create a new solution with a single-project, named *TodoApp*. Once the solution is ready, open it in your favorite IDE. + +{{end}} + +### Create the Database + +You can run the following command in the directory of your project to create the database and seed the initial data: + +```bash +dotnet run --migrate-database +``` + +This command will create the database and seed the initial data for you. Then you can run the application. + +### Run the Application + +{{if UI=="MVC" || UI=="BlazorServer"}} + +It is good to run the application before starting the development. Running the application is pretty straigth-forward, you can run the application with any IDE that supports .NET or by running the `dotnet run` CLI command in the directory of your project to see the initial UI: + +{{else if UI=="NG"}} + +It is good to run the application before starting the development. The solution has two main applications: + +* `TodoApp` (in the .NET solution) host the server-side HTTP API, so Angular application can consume it. (server-side application) +* `angular` folder contains the Angular application. (client-side application) + +Firstly, run the `TodoApp` project in your favorite IDE (or run the `dotnet run` CLI command on your project directory) to see the server-side HTTP API on the [Swagger UI](https://swagger.io/tools/swagger-ui/): + +![todo-swagger-ui-initial](../todo-swagger-ui-initial.png) + +You can explore and test your HTTP API with this UI. If it works, we can run the Angular client application. + +First, run the following command (or `yarn install`) to restore the NPM packages: + +````bash +npm install +```` + +It will take some time to install all packages. Then you can run the application using the following (or `yarn start`) command: + +````bash +npm start +```` + +This command takes time, but eventually runs and opens the application in your default browser: + +{{end}} + +![todo-ui-initial](../todo-ui-initial.png) + +You can click on the *Login* button, use `admin` as the username and `1q2w3E*` as the password to login to the application. + +All ready. We can start coding! + +## Defining Entities + +This application will have a single [entity](../../../Entities.md) and we can start by creating it. So, create a new `TodoItem` class under the **Entities** folder of the project. + +````csharp +using Volo.Abp.Domain.Entities; + +namespace TodoApp.Entities; + +public class TodoItem : BasicAggregateRoot +{ + public string Text { get; set; } +} +```` + +`BasicAggregateRoot` is the simplest base class to create root entities, and `Guid` is the primary key (`Id`) of the entity here. + +## Database Integration + +{{if DB=="EF"}} + +Next step is to setup the [Entity Framework Core](../../../Entity-Framework-Core.md) configuration. + +### Mapping Configuration + +Open the `TodoAppDbContext` class (it's under the **Data** folder) and add a new `DbSet` property to this class: + +````csharp +public DbSet TodoItems { get; set; } +```` + +Then navigate to the `OnModelCreating` method in the same class and add the mapping code for the `TodoItem ` entity: + +````csharp +protected override void OnModelCreating(ModelBuilder builder) +{ + base.OnModelCreating(builder); + + /* Include modules to your migration db context */ + + builder.ConfigurePermissionManagement(); + ... + + /* Configure your own tables/entities inside here */ + builder.Entity(b => + { + b.ToTable("TodoItems"); + }); +} +```` + +We've mapped the `TodoItem` entity to the `TodoItems` table in the database. The next step is creating a migration and apply the changes to the database. + +### Code First Migrations + +The startup solution is configured to use Entity Framework Core [Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations). Since we've changed the database mapping configuration, we should create a new migration and apply changes to the database. + +Open a command-line terminal in the directory of your project and type the following command: + +````bash +dotnet ef migrations add Added_TodoItem +```` + +This will add a new migration class to the project: + +![todo-efcore-migration](../todo-efcore-migration-single-layer.png) TODO: add this screenshot??? + +You can apply changes to the database using the following command, in the same command-line terminal: + +````bash +dotnet ef database update +```` + +{{else if DB=="Mongo"}} + +Next step is to setup the [MongoDB](../../../MongoDB.md) configuration. Open the `TodoAppMongoDbContext` class (it's under the **Data** folder) in your project and make the following changes: + +1. Add a new property to the class: + +````csharp +public IMongoCollection TodoItems => Collection(); +```` + +2. Add the following code inside the `CreateModel` method: + +````csharp +modelBuilder.Entity(b => +{ + b.CollectionName = "TodoItems"; +}); +```` + +{{end}} + +After the database integrations, now we can start to create application service methods and implement our use-cases. + +## Creating the Application Service Interface + +An [Application Service](../../Application-Services.md) is used to perform the use cases of the application. We need to perform the following use cases in this application: + +* Get the list of the todo items +* Create a new todo item +* Delete an existing todo item + +Let's start with defining an interface for the application service. Create a new `ITodoAppService` interface under the **Services** folder, as shown below: + +```csharp +using TodoApp.Services.Dtos; +using Volo.Abp.Application.Services; + +namespace TodoApp.Services; + +public interface ITodoAppService : IApplicationService +{ + Task> GetListAsync(); + Task CreateAsync(string text); + Task DeleteAsync(Guid id); +} +``` + +## Creating the Data Transfer Object (DTO) + +`GetListAsync` and `CreateAsync` methods return `TodoItemDto`. `ApplicationService` typically gets and returns DTOs ([Data Transfer Objects](../../../Data-Transfer-Objects.md)) instead of entities but we haven't created it yet. So, create a new `TodoItemDto` class under the **Dtos** folder (it's under the **Services** folder): + +```csharp +namespace TodoApp.Services.Dtos; + +public class TodoItemDto +{ + public Guid Id { get; set; } + public string Text { get; set; } +} +``` + +This is a very simple DTO class that have the same properties with our `TodoItem` entity. We are ready to implement the `ITodoAppService`. + +## Application Service Implementation + +Create a `TodoAppService` class under the **Services** folder of your project, as shown below: + +```csharp +using TodoApp.Entities; +using Volo.Abp.Application.Services; +using Volo.Abp.Domain.Repositories; + +namespace TodoApp.Services; + +public class TodoAppService : ApplicationService, ITodoAppService +{ + private readonly IRepository _todoItemRepository; + + public TodoAppService(IRepository todoItemRepository) + { + _todoItemRepository = todoItemRepository; + } + + // TODO: Implement the methods here... +} +``` + +This class inherits from the `ApplicationService` class of the ABP Framework and implements the `ITodoAppService` that created in the [Creating the Application Service Interface](#creating-the-application-service-interface) section. ABP provides default generic [repositories](../../../Repositories.md) for the entities. We can use them to perform the fundamental database operations. This class [injects](../../../Dependency-Injection.md) `IRepository`, which is the default repository for the `TodoItem` entity. We will use it to implement the use cases described before. + +### Getting Todo Items + +Let's start by implementing the `GetListAsync` method: + +````csharp +public async Task> GetListAsync() +{ + var items = await _todoItemRepository.GetListAsync(); + return items + .Select(item => new TodoItemDto + { + Id = item.Id, + Text = item.Text + }).ToList(); +} +```` + +We are simply getting the complete `TodoItem` list from the database, mapping them to `TodoItemDto` objects and returning as the result. + +#### Creating a New Todo Item + +Next method is `CreateAsync` and we can implement it as shown below: + +````csharp +public async Task CreateAsync(string text) +{ + var todoItem = await _todoItemRepository.InsertAsync( + new TodoItem {Text = text} + ); + + return new TodoItemDto + { + Id = todoItem.Id, + Text = todoItem.Text + }; +} +```` + +The repository's `InsertAsync` method inserts the given `TodoItem` to the database and returns the same `TodoItem` object. It also sets the `Id`, so we can use it on the returning object. We are simply returning a `TodoItemDto` by creating from the new `TodoItem` entity. + +#### Deleting a Todo Item + +Finally, we can implement the `DeleteAsync` as the following code block: + +````csharp +public async Task DeleteAsync(Guid id) +{ + await _todoItemRepository.DeleteAsync(id); +} +```` + +The application service is ready to be used from the UI layer. + +## User Interface Layer + +It is time to show the todo items on the UI! Before starting to write the code, it would be good to remember what we are trying to build. Here's a sample screenshot from the final UI: + +![todo-list](../todo-list.png) + +{{if UI=="MVC"}} + +{{else if UI=="BlazorServer"}} + +{{else if UI=="NG"}} + +{{end}} + +## Conclusion + +In this tutorial, we've built a very simple application to warm up with the ABP Framework. + +## Source Code + +You can find source code of the completed application [here](https://github.com/abpframework/abp-samples/tree/master/TodoApp). + +## See Also + +* [Web Application Development Tutorial](../Part-1.md) to see a real-life web application development in a layered architecture. \ No newline at end of file From 2b0e78f367182429686ee464b4af044699c13097 Mon Sep 17 00:00:00 2001 From: Hamza Albreem <94292623+braim23@users.noreply.github.com> Date: Tue, 17 May 2022 14:34:59 +0300 Subject: [PATCH 06/17] Update Application-Single-Layer.md --- .../Startup-Templates/Application-Single-Layer.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/en/Startup-Templates/Application-Single-Layer.md b/docs/en/Startup-Templates/Application-Single-Layer.md index 81f9e169df..ce194d7585 100644 --- a/docs/en/Startup-Templates/Application-Single-Layer.md +++ b/docs/en/Startup-Templates/Application-Single-Layer.md @@ -6,13 +6,13 @@ This template provides a single-layered application for a quick start with ABP F This document explains the **solution structure** and project in detail. -### What is the Difference Between the Application Startup Template? +### What is the Difference Between the Application Startup Templates? ABP's [Application Startup Template](Application.md) provides a well-organized and layered solution to create maintainable business applications based on the [Domain Driven Design](../Domain-Driven-Design.md) (DDD) practices. However, some developers find this template a little bit complex (or unnecessary) for simple and short-time applications. At this point, a single-layer application template has been created for such applications. This template has the same functionality, features and modules on runtime with the [Application Startup Template](Application.md) but the development model is minimal and everything is in a single project (`.csproj`). -## How to Start with? +## How to Start with It? You can use the [ABP CLI](../CLI.md) to create a new project using this startup template. Alternatively, you can directly create & download this startup template from the [Get Started](https://abp.io/get-started) page. How to download via CLI is explained in this section. @@ -65,7 +65,7 @@ abp new Acme.BookStore -t app-nolayers -d mongodb ## Solution Structure -If you don't specify any additional options while creating an `app-nolayers` template, you will have a solution like shown below: +If you don't specify any additional options while creating an `app-nolayers` template, you will have a solution as shown below: ![](../images/bookstore-single-layer-solution-structure.png) @@ -73,7 +73,7 @@ It's a single-layer template rather than a layered-architecture solution (like t ### Folder Structure -Since this template provides single-layer solution, we've separated concerns into folders instead of layers and you can see the pre-defined folders like shown below: +Since this template provides a single-layer solution, we've separated concerns into folders instead of layers and you can see the pre-defined folders as shown below: ![](../images/single-layer-folder-structure.png) @@ -94,10 +94,10 @@ dotnet run --migrate-database ### Angular UI -If you choose `Angular` as the UI framework, the solution is being separated into two folders: +If you choose `Angular` as the UI framework, the solution will be separated into two folders: -* `angular` folder contains the Angular UI application, the client-side code. -* `aspnet-core` folder contains the ASP.NET Core solution (a single project), the server-side code. +* An `angular` folder that contains the Angular UI application, the client-side code. +* An `aspnet-core` folder that contains the ASP.NET Core solution (a single project), the server-side code. The server-side is similar to the solution described in the [Solution Structure](#solution-structure) section above. This project serves the API, so the `Angular` application can consume it. From d7efec5df502926bfc534647a239cacd26618c21 Mon Sep 17 00:00:00 2001 From: Engincan VESKE Date: Tue, 17 May 2022 15:53:15 +0300 Subject: [PATCH 07/17] Update Quick Start documentation for `app-nolayers` --- docs/en/Tutorials/Todo/Single-Layer/Index.md | 205 ++++++++++++++++-- .../todo-efcore-migration-single-layer.png | Bin 0 -> 8866 bytes 2 files changed, 188 insertions(+), 17 deletions(-) create mode 100644 docs/en/Tutorials/Todo/Single-Layer/todo-efcore-migration-single-layer.png diff --git a/docs/en/Tutorials/Todo/Single-Layer/Index.md b/docs/en/Tutorials/Todo/Single-Layer/Index.md index fe70013e19..e902f3108c 100644 --- a/docs/en/Tutorials/Todo/Single-Layer/Index.md +++ b/docs/en/Tutorials/Todo/Single-Layer/Index.md @@ -12,7 +12,7 @@ This is a single-part quick-start tutorial to build a simple todo application wi ![todo-list](../todo-list.png) -You can find the source code of the completed application [here](TODO: sample's url???). +You can find the source code of the completed application [here](https://github.com/abpframework/abp-samples/tree/master/TodoApp). ## Pre-Requirements @@ -46,11 +46,11 @@ abp new TodoApp -t app-nolayers{{if UI=="BlazorServer"}} -u blazor-server{{else {{if UI=="NG"}} -This will create a new solution with a single-project, named *TodoApp* with `angular` and `aspnet-core` folders. Once the solution is ready, open the solution in your favorite IDE. +This will create a new solution with a single project, named *TodoApp* with `angular` and `aspnet-core` folders. Once the solution is ready, open the solution in your favorite IDE. {{else}} -This will create a new solution with a single-project, named *TodoApp*. Once the solution is ready, open it in your favorite IDE. +This will create a new solution with a single project, named *TodoApp*. Once the solution is ready, open it in your favorite IDE. {{end}} @@ -68,13 +68,13 @@ This command will create the database and seed the initial data for you. Then yo {{if UI=="MVC" || UI=="BlazorServer"}} -It is good to run the application before starting the development. Running the application is pretty straigth-forward, you can run the application with any IDE that supports .NET or by running the `dotnet run` CLI command in the directory of your project to see the initial UI: +It is good to run the application before starting the development. Running the application is pretty straight-forward, you can run the application with any IDE that supports .NET or by running the `dotnet run` CLI command in the directory of your project: {{else if UI=="NG"}} It is good to run the application before starting the development. The solution has two main applications: -* `TodoApp` (in the .NET solution) host the server-side HTTP API, so Angular application can consume it. (server-side application) +* `TodoApp` (in the .NET solution) hosts the server-side HTTP API, so the Angular application can consume it. (server-side application) * `angular` folder contains the Angular application. (client-side application) Firstly, run the `TodoApp` project in your favorite IDE (or run the `dotnet run` CLI command on your project directory) to see the server-side HTTP API on the [Swagger UI](https://swagger.io/tools/swagger-ui/): @@ -101,9 +101,9 @@ This command takes time, but eventually runs and opens the application in your d ![todo-ui-initial](../todo-ui-initial.png) -You can click on the *Login* button, use `admin` as the username and `1q2w3E*` as the password to login to the application. +You can click on the *Login* button and use `admin` as the username and `1q2w3E*` as the password to login to the application. -All ready. We can start coding! +All right. We can start coding! ## Defining Entities @@ -156,7 +156,7 @@ protected override void OnModelCreating(ModelBuilder builder) } ```` -We've mapped the `TodoItem` entity to the `TodoItems` table in the database. The next step is creating a migration and apply the changes to the database. +We've mapped the `TodoItem` entity to the `TodoItems` table in the database. The next step is to create a migration and apply the changes to the database. ### Code First Migrations @@ -168,11 +168,11 @@ Open a command-line terminal in the directory of your project and type the follo dotnet ef migrations add Added_TodoItem ```` -This will add a new migration class to the project: +This will add a new migration class to the project. You should see the migration in the **Migrations** folder. -![todo-efcore-migration](../todo-efcore-migration-single-layer.png) TODO: add this screenshot??? +![todo-efcore-migration](todo-efcore-migration-single-layer.png) -You can apply changes to the database using the following command, in the same command-line terminal: +Then, you can apply changes to the database using the following command, in the same command-line terminal: ````bash dotnet ef database update @@ -180,7 +180,7 @@ dotnet ef database update {{else if DB=="Mongo"}} -Next step is to setup the [MongoDB](../../../MongoDB.md) configuration. Open the `TodoAppMongoDbContext` class (it's under the **Data** folder) in your project and make the following changes: +The next step is to setup the [MongoDB](../../../MongoDB.md) configuration. Open the `TodoAppMongoDbContext` class (it's under the **Data** folder) in your project and make the following changes: 1. Add a new property to the class: @@ -239,7 +239,7 @@ public class TodoItemDto } ``` -This is a very simple DTO class that have the same properties with our `TodoItem` entity. We are ready to implement the `ITodoAppService`. +This is a very simple DTO class that has the same properties as the `TodoItem` entity. We are ready to implement the `ITodoAppService`. ## Application Service Implementation @@ -284,11 +284,11 @@ public async Task> GetListAsync() } ```` -We are simply getting the complete `TodoItem` list from the database, mapping them to `TodoItemDto` objects and returning as the result. +We are simply getting the `TodoItem` list from the database, mapping them to `TodoItemDto` objects and returning as the result. #### Creating a New Todo Item -Next method is `CreateAsync` and we can implement it as shown below: +The next method is `CreateAsync` and we can implement it as shown below: ````csharp public async Task CreateAsync(string text) @@ -328,8 +328,179 @@ It is time to show the todo items on the UI! Before starting to write the code, {{if UI=="MVC"}} +### Index.cshtml.cs + +Open the `Index.cshtml.cs` file in the `Pages` folder and replace the content with the following code block: + +```csharp +using TodoApp.Services; +using TodoApp.Services.Dtos; +using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; + +namespace TodoApp.Pages; + +public class IndexModel : AbpPageModel +{ + public List TodoItems { get; set; } + + private readonly ITodoAppService _todoAppService; + + public IndexModel(ITodoAppService todoAppService) + { + _todoAppService = todoAppService; + } + + public async Task OnGetAsync() + { + TodoItems = await _todoAppService.GetListAsync(); + } +} +``` + +This class uses the `ITodoAppService` to get the list of todo items and assign the `TodoItems` property. We will use it to render the todo items on the razor page. + +### Index.cshtml + +Open the `Index.cshtml` file in the `Pages` folder and replace it with the following content: + +```xml +@page +@model TodoApp.Pages.IndexModel + +@section styles { + +} +@section scripts { + +} + +
+ + + + TODO LIST + + + + +
+
+
+ +
+
+
+ +
+
+ +
    + @foreach (var todoItem in Model.TodoItems) + { +
  • + @todoItem.Text +
  • + } +
+
+
+
+``` + +We are using ABP's [card tag helper](../../UI/AspNetCore/Tag-Helpers/Cards.md) to create a simple card view. You could directly use the standard bootstrap HTML structure, however the ABP [tag helpers](../../UI/AspNetCore/Tag-Helpers/Index.md) make it much easier and type safe. + +This page imports a CSS and a JavaScript file, so we should also create them. + +### Index.js + +Create an `Index.js` file in the `Pages` folder and add the following content: + +````js +$(function () { + + // DELETING ITEMS ///////////////////////////////////////// + $('#TodoList').on('click', 'li i', function(){ + var $li = $(this).parent(); + var id = $li.attr('data-id'); + + todoApp.services.todo.delete(id).then(function(){ + $li.remove(); + abp.notify.info('Deleted the todo item.'); + }); + }); + + // CREATING NEW ITEMS ///////////////////////////////////// + $('#NewItemForm').submit(function(e){ + e.preventDefault(); + + var todoText = $('#NewItemText').val(); + todoApp.services.todo.create(todoText).then(function(result){ + $('
  • ') + .html(' ' + result.text) + .appendTo($('#TodoList')); + $('#NewItemText').val(''); + }); + }); +}); +```` + +In the first part, we are subscribing to the click events of the trash icons near the todo items, deleting the related item on the server and showing a notification on the UI. Also, we are removing the deleted item from the DOM, so we don't need to refresh the page. + +In the second part, we are creating a new todo item on the server. If it succeeds, we are then manipulating the DOM to insert a new `
  • ` element to the todo list. This way we don't need to refresh the whole page after creating a new todo item. + +The interesting part here is how we communicate with the server. See the [*Dynamic JavaScript Proxies & Auto API Controllers*](#dynamic-javascript-proxies--auto-api-controllers) section to understand how it works. But now, let's continue and complete the application. + +### Index.css + +As the final touch, create the `Index.css` file in the `Pages` folder and add the following content: + +````css +#TodoList{ + list-style: none; + margin: 0; + padding: 0; +} + +#TodoList li { + padding: 5px; + margin: 5px 0px; + border: 1px solid #cccccc; + background-color: #f5f5f5; +} + +#TodoList li i +{ + opacity: 0.5; +} + +#TodoList li i:hover +{ + opacity: 1; + color: #ff0000; + cursor: pointer; +} +```` + +This is a simple styling for the todo page. We believe that you can do much better :) + +Now, you can run the application again and see the result. + +### Dynamic JavaScript Proxies & Auto API Controllers + +In the `Index.js` file, we've used the `todoApp.services.todo.delete(...)` and `todoApp.services.todo.create(...)` functions to communicate with the server. These functions are dynamically created by the ABP Framework, thanks to the [Dynamic JavaScript Client Proxy](../../../UI/AspNetCore/Dynamic-JavaScript-Proxies.md) system. They perform HTTP API calls to the server and return a promise, so you can register a callback to the `then` function as we've done above. + +> **services** keyword comes from the namespace (`namespace TodoApp.Services;`). + +However, you may notice that we haven't created any API Controllers, so how does the server handle these requests? This question brings us to the [Auto API Controller](../../../API/Auto-API-Controllers.md) feature of the ABP Framework. It automatically converts the application services to **API Controllers** by convention. + +If you open the [Swagger UI](https://swagger.io/tools/swagger-ui/) by entering the `/swagger` URL in your application, you can see the Todo API: + +![todo-api](../todo-api.png) + + {{else if UI=="BlazorServer"}} + {{else if UI=="NG"}} {{end}} @@ -340,8 +511,8 @@ In this tutorial, we've built a very simple application to warm up with the ABP ## Source Code -You can find source code of the completed application [here](https://github.com/abpframework/abp-samples/tree/master/TodoApp). +You can find the source code of the completed application [here](https://github.com/abpframework/abp-samples/tree/master/TodoApp). ## See Also -* [Web Application Development Tutorial](../Part-1.md) to see a real-life web application development in a layered architecture. \ No newline at end of file +* Check the [Web Application Development Tutorial](../Part-1.md) to see a real-life web application development in a layered architecture using the [Application Startup Template](../../../Startup-Templates/Application.md). \ No newline at end of file diff --git a/docs/en/Tutorials/Todo/Single-Layer/todo-efcore-migration-single-layer.png b/docs/en/Tutorials/Todo/Single-Layer/todo-efcore-migration-single-layer.png new file mode 100644 index 0000000000000000000000000000000000000000..686d8a5f87be890ca789998d6cbafae931e872da GIT binary patch literal 8866 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 Date: Wed, 18 May 2022 12:25:07 +0300 Subject: [PATCH 08/17] Update Index.md --- docs/en/Tutorials/Todo/Single-Layer/Index.md | 26 ++++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/en/Tutorials/Todo/Single-Layer/Index.md b/docs/en/Tutorials/Todo/Single-Layer/Index.md index e902f3108c..dff251b881 100644 --- a/docs/en/Tutorials/Todo/Single-Layer/Index.md +++ b/docs/en/Tutorials/Todo/Single-Layer/Index.md @@ -77,7 +77,7 @@ It is good to run the application before starting the development. The solution * `TodoApp` (in the .NET solution) hosts the server-side HTTP API, so the Angular application can consume it. (server-side application) * `angular` folder contains the Angular application. (client-side application) -Firstly, run the `TodoApp` project in your favorite IDE (or run the `dotnet run` CLI command on your project directory) to see the server-side HTTP API on the [Swagger UI](https://swagger.io/tools/swagger-ui/): +Firstly, run the `TodoApp` project in your favorite IDE (or run the `dotnet run` CLI command on your project directory) to see the server-side HTTP API on [Swagger UI](https://swagger.io/tools/swagger-ui/): ![todo-swagger-ui-initial](../todo-swagger-ui-initial.png) @@ -89,7 +89,7 @@ First, run the following command (or `yarn install`) to restore the NPM packages npm install ```` -It will take some time to install all packages. Then you can run the application using the following (or `yarn start`) command: +It will take some time to install all the packages. Then you can run the application using the following (or `yarn start`) command: ````bash npm start @@ -130,7 +130,7 @@ Next step is to setup the [Entity Framework Core](../../../Entity-Framework-Core ### Mapping Configuration -Open the `TodoAppDbContext` class (it's under the **Data** folder) and add a new `DbSet` property to this class: +Open the `TodoAppDbContext` class (under the **Data** folder) and add a new `DbSet` property to this class: ````csharp public DbSet TodoItems { get; set; } @@ -158,7 +158,7 @@ protected override void OnModelCreating(ModelBuilder builder) We've mapped the `TodoItem` entity to the `TodoItems` table in the database. The next step is to create a migration and apply the changes to the database. -### Code First Migrations +### Code the First Migrations The startup solution is configured to use Entity Framework Core [Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations). Since we've changed the database mapping configuration, we should create a new migration and apply changes to the database. @@ -265,9 +265,9 @@ public class TodoAppService : ApplicationService, ITodoAppService } ``` -This class inherits from the `ApplicationService` class of the ABP Framework and implements the `ITodoAppService` that created in the [Creating the Application Service Interface](#creating-the-application-service-interface) section. ABP provides default generic [repositories](../../../Repositories.md) for the entities. We can use them to perform the fundamental database operations. This class [injects](../../../Dependency-Injection.md) `IRepository`, which is the default repository for the `TodoItem` entity. We will use it to implement the use cases described before. +This class inherits from the `ApplicationService` class of the ABP Framework and implements the `ITodoAppService` that was created in the [Creating the Application Service Interface](#creating-the-application-service-interface) section. ABP provides default generic [repositories](../../../Repositories.md) for the entities. We can use them to perform the fundamental database operations. This class [injects](../../../Dependency-Injection.md) `IRepository`, which is the default repository for the `TodoItem` entity. We will use it to implement the use cases described before. -### Getting Todo Items +### Getting the Todo Items Let's start by implementing the `GetListAsync` method: @@ -284,7 +284,7 @@ public async Task> GetListAsync() } ```` -We are simply getting the `TodoItem` list from the database, mapping them to `TodoItemDto` objects and returning as the result. +We are simply getting the `TodoItem` list from the database, mapping them to the `TodoItemDto` objects and returning as the result. #### Creating a New Todo Item @@ -357,7 +357,7 @@ public class IndexModel : AbpPageModel } ``` -This class uses the `ITodoAppService` to get the list of todo items and assign the `TodoItems` property. We will use it to render the todo items on the razor page. +This class uses `ITodoAppService` to get the list of todo items and assign the `TodoItems` property. We will use it to render the todo items on the razor page. ### Index.cshtml @@ -444,15 +444,15 @@ $(function () { }); ```` -In the first part, we are subscribing to the click events of the trash icons near the todo items, deleting the related item on the server and showing a notification on the UI. Also, we are removing the deleted item from the DOM, so we don't need to refresh the page. +In the first part, we subscribed to the click events of the trash icons near the todo items, deleted the related item on the server and showed a notification on the UI. Also, we removed the deleted item from the DOM, so we wouldn't need to refresh the page. -In the second part, we are creating a new todo item on the server. If it succeeds, we are then manipulating the DOM to insert a new `
  • ` element to the todo list. This way we don't need to refresh the whole page after creating a new todo item. +In the second part, we created a new todo item on the server. If it succeeded, we would then manipulate the DOM to insert a new `
  • ` element to the todo list. This way, we wouldn't need to refresh the whole page after creating a new todo item. The interesting part here is how we communicate with the server. See the [*Dynamic JavaScript Proxies & Auto API Controllers*](#dynamic-javascript-proxies--auto-api-controllers) section to understand how it works. But now, let's continue and complete the application. ### Index.css -As the final touch, create the `Index.css` file in the `Pages` folder and add the following content: +As for the final touch, create the `Index.css` file in the `Pages` folder and add the following content: ````css #TodoList{ @@ -493,7 +493,7 @@ In the `Index.js` file, we've used the `todoApp.services.todo.delete(...)` and ` However, you may notice that we haven't created any API Controllers, so how does the server handle these requests? This question brings us to the [Auto API Controller](../../../API/Auto-API-Controllers.md) feature of the ABP Framework. It automatically converts the application services to **API Controllers** by convention. -If you open the [Swagger UI](https://swagger.io/tools/swagger-ui/) by entering the `/swagger` URL in your application, you can see the Todo API: +If you open [Swagger UI](https://swagger.io/tools/swagger-ui/) by entering the `/swagger` URL in your application, you can see the Todo API: ![todo-api](../todo-api.png) @@ -515,4 +515,4 @@ You can find the source code of the completed application [here](https://github. ## See Also -* Check the [Web Application Development Tutorial](../Part-1.md) to see a real-life web application development in a layered architecture using the [Application Startup Template](../../../Startup-Templates/Application.md). \ No newline at end of file +* Check the [Web Application Development Tutorial](../Part-1.md) to see a real-life web application development in a layered architecture using the [Application Startup Template](../../../Startup-Templates/Application.md). From d9e01eb6077baafe36ab5693345ec1592971b46e Mon Sep 17 00:00:00 2001 From: Engincan VESKE <43685404+EngincanV@users.noreply.github.com> Date: Wed, 18 May 2022 12:27:48 +0300 Subject: [PATCH 09/17] Update Index.md --- docs/en/Tutorials/Todo/Single-Layer/Index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Tutorials/Todo/Single-Layer/Index.md b/docs/en/Tutorials/Todo/Single-Layer/Index.md index dff251b881..2ae6b49df7 100644 --- a/docs/en/Tutorials/Todo/Single-Layer/Index.md +++ b/docs/en/Tutorials/Todo/Single-Layer/Index.md @@ -158,7 +158,7 @@ protected override void OnModelCreating(ModelBuilder builder) We've mapped the `TodoItem` entity to the `TodoItems` table in the database. The next step is to create a migration and apply the changes to the database. -### Code the First Migrations +### Code First Migrations The startup solution is configured to use Entity Framework Core [Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations). Since we've changed the database mapping configuration, we should create a new migration and apply changes to the database. From 554884e822db655cb4fb87522ed363c143b4f86a Mon Sep 17 00:00:00 2001 From: Engincan VESKE Date: Fri, 20 May 2022 15:29:26 +0300 Subject: [PATCH 10/17] Update Index.md --- docs/en/Tutorials/Todo/Single-Layer/Index.md | 185 ++++++++++++++----- 1 file changed, 142 insertions(+), 43 deletions(-) diff --git a/docs/en/Tutorials/Todo/Single-Layer/Index.md b/docs/en/Tutorials/Todo/Single-Layer/Index.md index 2ae6b49df7..a53ff0a15a 100644 --- a/docs/en/Tutorials/Todo/Single-Layer/Index.md +++ b/docs/en/Tutorials/Todo/Single-Layer/Index.md @@ -12,7 +12,7 @@ This is a single-part quick-start tutorial to build a simple todo application wi ![todo-list](../todo-list.png) -You can find the source code of the completed application [here](https://github.com/abpframework/abp-samples/tree/master/TodoApp). +You can find the source code of the completed application [here](https://github.com/abpframework/abp-samples/tree/master/TodoApp-SingleLayer). ## Pre-Requirements @@ -81,15 +81,9 @@ Firstly, run the `TodoApp` project in your favorite IDE (or run the `dotnet run` ![todo-swagger-ui-initial](../todo-swagger-ui-initial.png) -You can explore and test your HTTP API with this UI. If it works, we can run the Angular client application. +You can explore and test your HTTP API with this UI. If it works, then we can run the Angular client application. -First, run the following command (or `yarn install`) to restore the NPM packages: - -````bash -npm install -```` - -It will take some time to install all the packages. Then you can run the application using the following (or `yarn start`) command: +You can run the application using the following (or `yarn start`) command: ````bash npm start @@ -107,7 +101,7 @@ All right. We can start coding! ## Defining Entities -This application will have a single [entity](../../../Entities.md) and we can start by creating it. So, create a new `TodoItem` class under the **Entities** folder of the project. +This application will have a single [entity](../../../Entities.md) and we can start by creating it. So, create a new `TodoItem` class under the **Entities** folder of the project: ````csharp using Volo.Abp.Domain.Entities; @@ -136,7 +130,7 @@ Open the `TodoAppDbContext` class (under the **Data** folder) and add a new `DbS public DbSet TodoItems { get; set; } ```` -Then navigate to the `OnModelCreating` method in the same class and add the mapping code for the `TodoItem ` entity: +Then navigate to the `OnModelCreating` method in the same class and add the following mapping code for the `TodoItem ` entity: ````csharp protected override void OnModelCreating(ModelBuilder builder) @@ -168,7 +162,7 @@ Open a command-line terminal in the directory of your project and type the follo dotnet ef migrations add Added_TodoItem ```` -This will add a new migration class to the project. You should see the migration in the **Migrations** folder. +This will add a new migration class to the project. You should see the new migration in the **Migrations** folder. ![todo-efcore-migration](todo-efcore-migration-single-layer.png) @@ -180,7 +174,7 @@ dotnet ef database update {{else if DB=="Mongo"}} -The next step is to setup the [MongoDB](../../../MongoDB.md) configuration. Open the `TodoAppMongoDbContext` class (it's under the **Data** folder) in your project and make the following changes: +The next step is to setup the [MongoDB](../../../MongoDB.md) configuration. Open the `TodoAppDbContext` class (under the **Data** folder) in your project and make the following changes: 1. Add a new property to the class: @@ -201,7 +195,7 @@ modelBuilder.Entity(b => After the database integrations, now we can start to create application service methods and implement our use-cases. -## Creating the Application Service Interface +## Creating the Application Service An [Application Service](../../Application-Services.md) is used to perform the use cases of the application. We need to perform the following use cases in this application: @@ -209,25 +203,11 @@ An [Application Service](../../Application-Services.md) is used to perform the u * Create a new todo item * Delete an existing todo item -Let's start with defining an interface for the application service. Create a new `ITodoAppService` interface under the **Services** folder, as shown below: - -```csharp -using TodoApp.Services.Dtos; -using Volo.Abp.Application.Services; - -namespace TodoApp.Services; - -public interface ITodoAppService : IApplicationService -{ - Task> GetListAsync(); - Task CreateAsync(string text); - Task DeleteAsync(Guid id); -} -``` +Before starting to implement these use cases, first we need to create DTOs. -## Creating the Data Transfer Object (DTO) +### Creating the Data Transfer Object (DTO) -`GetListAsync` and `CreateAsync` methods return `TodoItemDto`. `ApplicationService` typically gets and returns DTOs ([Data Transfer Objects](../../../Data-Transfer-Objects.md)) instead of entities but we haven't created it yet. So, create a new `TodoItemDto` class under the **Dtos** folder (it's under the **Services** folder): +`ApplicationService` typically gets and returns DTOs ([Data Transfer Objects](../../../Data-Transfer-Objects.md)) instead of entities. So, create a new `TodoItemDto` class under the **Dtos** folder (under the **Services** folder): ```csharp namespace TodoApp.Services.Dtos; @@ -239,9 +219,9 @@ public class TodoItemDto } ``` -This is a very simple DTO class that has the same properties as the `TodoItem` entity. We are ready to implement the `ITodoAppService`. +* This is a very simple DTO class that has the same properties as the `TodoItem` entity. Now, we are ready to implement our use-cases. -## Application Service Implementation +### Application Service Implementation Create a `TodoAppService` class under the **Services** folder of your project, as shown below: @@ -252,7 +232,7 @@ using Volo.Abp.Domain.Repositories; namespace TodoApp.Services; -public class TodoAppService : ApplicationService, ITodoAppService +public class TodoAppService : ApplicationService { private readonly IRepository _todoItemRepository; @@ -265,9 +245,9 @@ public class TodoAppService : ApplicationService, ITodoAppService } ``` -This class inherits from the `ApplicationService` class of the ABP Framework and implements the `ITodoAppService` that was created in the [Creating the Application Service Interface](#creating-the-application-service-interface) section. ABP provides default generic [repositories](../../../Repositories.md) for the entities. We can use them to perform the fundamental database operations. This class [injects](../../../Dependency-Injection.md) `IRepository`, which is the default repository for the `TodoItem` entity. We will use it to implement the use cases described before. +This class inherits from the `ApplicationService` class of the ABP Framework and implements our use-cases. ABP provides default generic [repositories](../../../Repositories.md) for the entities. We can use them to perform the fundamental database operations. This class [injects](../../../Dependency-Injection.md) `IRepository`, which is the default repository for the `TodoItem` entity. We will use it to implement the use cases (`GetListAsync`, `CreateAsync` and `DeleteAsync`) described before. -### Getting the Todo Items +#### Getting the Todo Items Let's start by implementing the `GetListAsync` method: @@ -318,9 +298,9 @@ public async Task DeleteAsync(Guid id) } ```` -The application service is ready to be used from the UI layer. +The application service is ready to be used from the UI layer. So, let's implement it. -## User Interface Layer +## User Interface It is time to show the todo items on the UI! Before starting to write the code, it would be good to remember what we are trying to build. Here's a sample screenshot from the final UI: @@ -343,9 +323,9 @@ public class IndexModel : AbpPageModel { public List TodoItems { get; set; } - private readonly ITodoAppService _todoAppService; + private readonly TodoAppService _todoAppService; - public IndexModel(ITodoAppService todoAppService) + public IndexModel(TodoAppService todoAppService) { _todoAppService = todoAppService; } @@ -357,7 +337,7 @@ public class IndexModel : AbpPageModel } ``` -This class uses `ITodoAppService` to get the list of todo items and assign the `TodoItems` property. We will use it to render the todo items on the razor page. +This class uses `TodoAppService` to get the list of todo items and assign the `TodoItems` property. We will use it to render the todo items on the razor page. ### Index.cshtml @@ -497,9 +477,128 @@ If you open [Swagger UI](https://swagger.io/tools/swagger-ui/) by entering the ` ![todo-api](../todo-api.png) - {{else if UI=="BlazorServer"}} +### Index.razor.cs + +Open the `Index.razor.cs` file in the `Pages` folder and replace the content with the following code block: + +```csharp +using Microsoft.AspNetCore.Components; +using TodoApp.Services; +using TodoApp.Services.Dtos; + +namespace TodoApp.Pages; + +public partial class Index +{ + [Inject] + private TodoAppService TodoAppService { get; set; } + + private List TodoItems { get; set; } = new List(); + private string NewTodoText { get; set; } + + protected override async Task OnInitializedAsync() + { + TodoItems = await TodoAppService.GetListAsync(); + } + + private async Task Create() + { + var result = await TodoAppService.CreateAsync(NewTodoText); + TodoItems.Add(result); + NewTodoText = null; + } + + private async Task Delete(TodoItemDto todoItem) + { + await TodoAppService.DeleteAsync(todoItem.Id); + await Notify.Info("Deleted the todo item."); + TodoItems.Remove(todoItem); + } +} +``` + +This class uses the `TodoAppService` to get the list of todo items. It manipulates the `TodoItems` list after create and delete operations. This way, we don't need to refresh the whole todo list from the server. + +### Index.razor + +Open the `Index.razor` file in the `Pages` folder and replace the content with the following code block: + +```xml +@page "/" +@inherits TodoAppComponentBase + +
    + + + + TODO LIST + + + + +
    +
    +
    + +
    +
    +
    + +
    +
    + +
      + @foreach (var todoItem in TodoItems) + { +
    • + + @todoItem.Text +
    • + } +
    +
    +
    +
    +``` + +### Index.razor.css + +As the final touch, open the `Index.razor.css` file in the `Pages` folder and replace it with the following content: + +````css +#TodoList{ + list-style: none; + margin: 0; + padding: 0; +} + +#TodoList li { + padding: 5px; + margin: 5px 0px; + border: 1px solid #cccccc; + background-color: #f5f5f5; +} + +#TodoList li i +{ + opacity: 0.5; +} + +#TodoList li i:hover +{ + opacity: 1; + color: #ff0000; + cursor: pointer; +} +```` + +This is a simple styling for the todo page. We believe that you can do much better :) + +Now, you can run the application again to see the result. + {{else if UI=="NG"}} @@ -511,7 +610,7 @@ In this tutorial, we've built a very simple application to warm up with the ABP ## Source Code -You can find the source code of the completed application [here](https://github.com/abpframework/abp-samples/tree/master/TodoApp). +You can find the source code of the completed application [here](https://github.com/abpframework/abp-samples/tree/master/TodoApp-SingleLayer). ## See Also From 946a54b4c57dc948d8cd7c3e2fcd93487b0ba75c Mon Sep 17 00:00:00 2001 From: Engincan VESKE Date: Sat, 21 May 2022 11:43:11 +0300 Subject: [PATCH 11/17] Update Index.md --- docs/en/Tutorials/Todo/Single-Layer/Index.md | 145 ++++++++++++++++++- 1 file changed, 144 insertions(+), 1 deletion(-) diff --git a/docs/en/Tutorials/Todo/Single-Layer/Index.md b/docs/en/Tutorials/Todo/Single-Layer/Index.md index a53ff0a15a..244b30acb9 100644 --- a/docs/en/Tutorials/Todo/Single-Layer/Index.md +++ b/docs/en/Tutorials/Todo/Single-Layer/Index.md @@ -599,9 +599,152 @@ This is a simple styling for the todo page. We believe that you can do much bett Now, you can run the application again to see the result. - {{else if UI=="NG"}} +### Service Proxy Generation + +ABP provides a handy feature to automatically create client-side services to easily consume HTTP APIs provided by the server. + +You first need to run the `TodoApp` project since the proxy generator reads API definitions from the server application. + +Once you run the `TodoApp` project (**Swagger API Definition** will be shown), open a command-line terminal in the directory of `angular` folder and run the following command: + +```bash +abp generate-proxy -t ng +``` + +If everything goes well, it should generate an output as shown below: + +```bash +CREATE src/app/proxy/generate-proxy.json (182755 bytes) +CREATE src/app/proxy/README.md (1000 bytes) +CREATE src/app/proxy/services/todo.service.ts (833 bytes) +CREATE src/app/proxy/services/dtos/models.ts (71 bytes) +CREATE src/app/proxy/services/dtos/index.ts (26 bytes) +CREATE src/app/proxy/services/index.ts (81 bytes) +CREATE src/app/proxy/index.ts (61 bytes) +``` + +Then, we can use the `TodoService` to use the server-side HTTP APIs, as we'll do in the next section. + +### home.component.ts + +Open the `/angular/src/app/home/home.component.ts` file and replace its content with the following code block: + +```ts +import { ToasterService } from "@abp/ng.theme.shared"; +import { Component, OnInit } from '@angular/core'; +import { TodoItemDto } from "@proxy/services/dtos"; +import { TodoService } from "@proxy/services"; + +@Component({ + selector: 'app-home', + templateUrl: './home.component.html', + styleUrls: ['./home.component.scss'], +}) + +export class HomeComponent implements OnInit { + + todoItems: TodoItemDto[]; + newTodoText: string; + + constructor( + private todoService: TodoService, + private toasterService: ToasterService) + { } + + ngOnInit(): void { + this.todoService.getList().subscribe(response => { + this.todoItems = response; + }); + } + + create(): void{ + this.todoService.create(this.newTodoText).subscribe((result) => { + this.todoItems = this.todoItems.concat(result); + this.newTodoText = null; + }); + } + + delete(id: string): void { + this.todoService.delete(id).subscribe(() => { + this.todoItems = this.todoItems.filter(item => item.id !== id); + this.toasterService.info('Deleted the todo item.'); + }); + } +} +``` + +We've used `TodoService` to get the list of todo items and assigned the returning value to the `todoItems` array. We've also added `create` and `delete` methods. These methods will be used on the view side. + +### home.component.html + +Open the `/angular/src/app/home/home.component.html` file and replace its content with the following code block: + +````html +
    +
    +
    +
    TODO LIST
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    + +
      +
    • + {%{{{ todoItem.text }}}%} +
    • +
    +
    +
    +
    +```` + +### home.component.scss + +As the final touch, open the `/angular/src/app/home/home.component.scss` file and replace its content with the following code block: + +````css +#TodoList{ + list-style: none; + margin: 0; + padding: 0; +} + +#TodoList li { + padding: 5px; + margin: 5px 0px; + border: 1px solid #cccccc; + background-color: #f5f5f5; +} + +#TodoList li i +{ + opacity: 0.5; +} + +#TodoList li i:hover +{ + opacity: 1; + color: #ff0000; + cursor: pointer; +} +```` + +This is a simple styling for the todo page. We believe that you can do much better :) + +Now, you can run the application again to see the result. + {{end}} ## Conclusion From 456afa8c3a16c7cba670c214b7865c5eb381df84 Mon Sep 17 00:00:00 2001 From: Engincan VESKE Date: Sat, 21 May 2022 12:14:27 +0300 Subject: [PATCH 12/17] Update Index.md --- docs/en/Tutorials/Todo/Single-Layer/Index.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/en/Tutorials/Todo/Single-Layer/Index.md b/docs/en/Tutorials/Todo/Single-Layer/Index.md index 244b30acb9..9f4927f5e4 100644 --- a/docs/en/Tutorials/Todo/Single-Layer/Index.md +++ b/docs/en/Tutorials/Todo/Single-Layer/Index.md @@ -348,10 +348,10 @@ Open the `Index.cshtml` file in the `Pages` folder and replace it with the follo @model TodoApp.Pages.IndexModel @section styles { - + } @section scripts { - + }
    @@ -391,9 +391,9 @@ We are using ABP's [card tag helper](../../UI/AspNetCore/Tag-Helpers/Cards.md) t This page imports a CSS and a JavaScript file, so we should also create them. -### Index.js +### Index.cshtml.js -Create an `Index.js` file in the `Pages` folder and add the following content: +Open the `Index.cshtml.js` file in the `Pages` folder and replace with the following content: ````js $(function () { @@ -430,9 +430,9 @@ In the second part, we created a new todo item on the server. If it succeeded, w The interesting part here is how we communicate with the server. See the [*Dynamic JavaScript Proxies & Auto API Controllers*](#dynamic-javascript-proxies--auto-api-controllers) section to understand how it works. But now, let's continue and complete the application. -### Index.css +### Index.cshtml.css -As for the final touch, create the `Index.css` file in the `Pages` folder and add the following content: +As for the final touch, open the `Index.cshtml.css` file in the `Pages` folder and replace with the following content: ````css #TodoList{ @@ -467,9 +467,9 @@ Now, you can run the application again and see the result. ### Dynamic JavaScript Proxies & Auto API Controllers -In the `Index.js` file, we've used the `todoApp.services.todo.delete(...)` and `todoApp.services.todo.create(...)` functions to communicate with the server. These functions are dynamically created by the ABP Framework, thanks to the [Dynamic JavaScript Client Proxy](../../../UI/AspNetCore/Dynamic-JavaScript-Proxies.md) system. They perform HTTP API calls to the server and return a promise, so you can register a callback to the `then` function as we've done above. +In the `Index.cshtml.js` file, we've used the `todoApp.services.todo.delete(...)` and `todoApp.services.todo.create(...)` functions to communicate with the server. These functions are dynamically created by the ABP Framework, thanks to the [Dynamic JavaScript Client Proxy](../../../UI/AspNetCore/Dynamic-JavaScript-Proxies.md) system. They perform HTTP API calls to the server and return a promise, so you can register a callback to the `then` function as we've done above. -> **services** keyword comes from the namespace (`namespace TodoApp.Services;`). +> **services** keyword comes from the namespace (`namespace TodoApp.Services;`). It's a naming convention. However, you may notice that we haven't created any API Controllers, so how does the server handle these requests? This question brings us to the [Auto API Controller](../../../API/Auto-API-Controllers.md) feature of the ABP Framework. It automatically converts the application services to **API Controllers** by convention. From 247c0ea12183481548ccc7c996ef49d217b49e99 Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Tue, 31 May 2022 08:32:12 +0300 Subject: [PATCH 13/17] Update saving without any permissions localization --- .../Components/PermissionManagementModal.razor.cs | 2 +- .../Volo/Abp/PermissionManagement/Localization/Domain/en.json | 2 +- .../Volo/Abp/PermissionManagement/Localization/Domain/tr.json | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs index e81eed859d..b477de008d 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs @@ -133,7 +133,7 @@ public partial class PermissionManagementModal if (!updateDto.Permissions.Any(x => x.IsGranted)) { - if (!await Message.Confirm(L["RemoveAllPermissionsWarningMessage"].Value)) + if (!await Message.Confirm(L["SaveWithoutAnyPermissionsWarningMessage"].Value)) { return; } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en.json index 98f3a60216..8fbe3e9eab 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en.json @@ -6,6 +6,6 @@ "All": "All", "SelectAllInAllTabs": "Grant all permissions", "SelectAllInThisTab": "Select all", - "RemoveAllPermissionsWarningMessage": "Are you sure you want to remove all permissions?" + "SaveWithoutAnyPermissionsWarningMessage": "Are you sure you want to save without any permissions?" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/tr.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/tr.json index e462d91a51..ca6802de1e 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/tr.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/tr.json @@ -5,6 +5,7 @@ "OnlyProviderPermissons": "Sadece bu sağlayıcı", "All": "Hepsi", "SelectAllInAllTabs": "Tüm izinleri ver", - "SelectAllInThisTab": "Hepsini seç" + "SelectAllInThisTab": "Hepsini seç", + "SaveWithoutAnyPermissionsWarningMessage": "Hiçbir izin olmadan kaydetmek istediğinize emin misiniz??" } } \ No newline at end of file From 4a7dcf000b6fe8877c11d5ac33c0509ca876da6f Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Tue, 31 May 2022 08:43:34 +0300 Subject: [PATCH 14/17] Update permission-management-modal.js --- .../AbpPermissionManagement/permission-management-modal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/permission-management-modal.js b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/permission-management-modal.js index 208047314c..9577d38696 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/permission-management-modal.js +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/permission-management-modal.js @@ -265,7 +265,7 @@ var abp = abp || {}; e.preventDefault(); if(!$form.find("input:checked").length > 0) { - abp.message.confirm(l("RemoveAllPermissionsWarningMessage")) + abp.message.confirm(l("SaveWithoutAnyPermissionsWarningMessage")) .then(function (confirmed) { if(confirmed) { $form.submit(); From 55931dc2a3f91e933f50204850b1cb146c325c76 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Tue, 31 May 2022 09:32:11 +0300 Subject: [PATCH 15/17] Cli: Log special information on Dll not found exception --- .../Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs index aabd5c6dfd..6ee6a74a47 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs @@ -132,6 +132,11 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency if (!response.IsNullOrWhiteSpace()) { Logger.LogError(response); + + if (response.Contains("Commercial.SuiteTemplates.dll")) + { + Logger.LogInformation("The solution should be built before generating an entity! Use `dotnet build` to build your solution."); + } } else { From e888bd267d5358a338b0ffc12509a622e63db7cd Mon Sep 17 00:00:00 2001 From: selman koc <64414348+skoc10@users.noreply.github.com> Date: Tue, 31 May 2022 11:57:37 +0300 Subject: [PATCH 16/17] Update common.props --- common.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common.props b/common.props index 9b4b018b94..6be2bd8531 100644 --- a/common.props +++ b/common.props @@ -1,7 +1,7 @@ latest - 5.3.0-rc.2 + 5.3.0-rc.3 $(NoWarn);CS1591;CS0436 https://abp.io/assets/abp_nupkg.png https://abp.io/ From 5d853d37f86fcdd884149fc67dca3dfbf47170b0 Mon Sep 17 00:00:00 2001 From: Engincan VESKE <43685404+EngincanV@users.noreply.github.com> Date: Tue, 31 May 2022 13:26:35 +0300 Subject: [PATCH 17/17] Update tr.json --- .../Volo/Abp/PermissionManagement/Localization/Domain/tr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/tr.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/tr.json index ca6802de1e..dececfcce5 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/tr.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/tr.json @@ -6,6 +6,6 @@ "All": "Hepsi", "SelectAllInAllTabs": "Tüm izinleri ver", "SelectAllInThisTab": "Hepsini seç", - "SaveWithoutAnyPermissionsWarningMessage": "Hiçbir izin olmadan kaydetmek istediğinize emin misiniz??" + "SaveWithoutAnyPermissionsWarningMessage": "Hiçbir izin olmadan kaydetmek istediğinize emin misiniz?" } -} \ No newline at end of file +}