# Quick Start ````json //[doc-params] { "UI": ["MVC", "Blazor", "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](https://github.com/abpframework/abp-samples/tree/master/TodoApp). This documentation has a video tutorial on **YouTube**!! You can watch it here: {{if UI=="MVC" && DB =="EF"}} {{else if UI=="Blazor" && DB=="EF"}} {{else if UI=="BlazorServer" && DB=="EF"}} {{else if UI=="NG" && DB=="EF"}} {{else if UI=="MVC" && DB=="Mongo"}} {{else if UI=="BlazorServer" && DB=="Mongo"}} {{else if UI=="Blazor" && DB=="Mongo"}} {{else if UI=="NG" && DB=="Mongo"}} {{end}} ## Pre-Requirements * An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 7.0+](https://dotnet.microsoft.com/download/dotnet) development. * [Node v16.x](https://nodejs.org/) {{if DB=="Mongo"}} * [MongoDB Server 4.0+](https://docs.mongodb.com/manual/administration/install-community/) {{end}} ## Install ABP CLI Tool We will use the [ABP CLI](../../CLI.md) to create new ABP solutions. You can run the following command on a terminal window to install this dotnet tool: ````bash dotnet tool install -g Volo.Abp.Cli ```` ## Create Your ABP Solution Create an empty folder, open a command-line terminal and execute the following command in the terminal: ````bash abp new TodoApp{{if UI=="Blazor"}} -u blazor{{else 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, named *TodoApp* with `angular` and `aspnet-core` folders. Once the solution is ready, open the ASP.NET Core solution in your favorite IDE. {{else}} This will create a new solution, named *TodoApp*. Once the solution is ready, open it in your favorite IDE. {{end}} ### Create the Database If you are using Visual Studio, right click on the `TodoApp.DbMigrator` project, select *Set as StartUp Project*, then hit *Ctrl+F5* to run it without debugging. It will create the initial database and seed the initial data. {{if DB=="EF"}} > Some IDEs (e.g. Rider) may have problems for the first run since *DbMigrator* adds the initial migration and re-compiles the project. In this case, open a command-line terminal in the folder of the `.DbMigrator` project and execute the `dotnet run` command. {{end}} ### Before Running the Application #### Installing the Client-Side Packages [ABP CLI](../../CLI.md) runs the `abp install-libs` command behind the scenes to install the required NPM packages for your solution while creating the application. However, sometimes this command might need to be manually run. For example, you need to run this command, if you have cloned the application, or the resources from *node_modules* folder didn't copy to *wwwroot/libs* folder, or if you have added a new client-side package dependency to your solution. For such cases, run the `abp install-libs` command on the root directory of your solution to install all required NPM packages: ```bash abp install-libs ``` > We suggest you install [Yarn](https://classic.yarnpkg.com/) to prevent possible package inconsistencies, if you haven't installed it yet. {{if UI=="Blazor" || UI="BlazorServer"}} #### Bundling and Minification `abp bundle` command offers bundling and minification support for client-side resources (JavaScript and CSS files) for Blazor projects. This command automatically run when you create a new solution with the [ABP CLI](../../CLI.md). However, sometimes you might need to run this command manually. To update script & style references without worrying about dependencies, ordering, etc. in a project, you can run this command in the directory of your blazor application: ```bash abp bundle ``` > For more details about managing style and script references in Blazor or MAUI Blazor apps, see [Managing Global Scripts & Styles](../../UI/Blazor/Global-Scripts-Styles.md). {{end}} ### Run the Application {{if UI=="MVC" || UI=="BlazorServer"}} It is good to run the application before starting the development. Ensure the {{if UI=="BlazorServer"}}`TodoApp.Blazor`{{else}}`TodoApp.Web`{{end}} project is the startup project, then run the application (Ctrl+F5 in Visual Studio) to see the initial UI: {{else if UI=="Blazor"}} It is good to run the application before starting the development. The solution has two main applications; * `TodoApp.HttpApi.Host` hosts the server-side HTTP API. * `TodoApp.Blazor` is the client-side Blazor WebAssembly application. Ensure the `TodoApp.HttpApi.Host` project is the startup project, then run the application (Ctrl+F5 in Visual Studio) 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. Now, we can set the `TodoApp.Blazor` as the startup project and run it to open the actual Blazor application UI: {{else if UI=="NG"}} It is good to run the application before starting the development. The solution has two main applications: * `TodoApp.HttpApi.Host` (in the .NET solution) host the server-side HTTP API. * `angular` folder contains the Angular application. Ensure that the `TodoApp.HttpApi.Host` project is the startup project, then run the application (Ctrl+F5 in Visual Studio) 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. You can run the application using the following 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! ## Domain Layer This application has a single [entity](../../Entities.md) and we'll start by creating it. Create a new `TodoItem` class inside the *TodoApp.Domain* project: ````csharp using System; using Volo.Abp.Domain.Entities; namespace TodoApp { 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 in the `EntityFrameworkCore` folder of the *TodoApp.EntityFrameworkCore* project and add a new `DbSet` property to this class: ````csharp public DbSet TodoItems { get; set; } ```` Then navigate to the `OnModelCreating` method in the `TodoAppDbContext` 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. ### 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 the *TodoApp.EntityFrameworkCore* 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.png) You can apply changes to the database using the following command, in the same command-line terminal: ````bash dotnet ef database update ```` > If you are using Visual Studio, you may want to use the `Add-Migration Added_TodoItem` and `Update-Database` commands in the *Package Manager Console (PMC)*. In this case, ensure that {{if UI=="MVC"}}`TodoApp.Web`{{else if UI=="BlazorServer"}}`TodoApp.Blazor`{{else if UI=="Blazor" || UI=="NG"}}`TodoApp.HttpApi.Host`{{end}} is the startup project and `TodoApp.EntityFrameworkCore` is the *Default Project* in PMC. {{else if DB=="Mongo"}} Next step is to setup the [MongoDB](../../MongoDB.md) configuration. Open the `TodoAppMongoDbContext` class in the `MongoDb` folder of the *TodoApp.MongoDB* 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}} Now, we can use the ABP repositories to save and retrieve the todo items, as we'll do in the next section. ## Application Layer An [Application Service](../../Application-Services.md) is used to perform the use cases of the application. We need to perform the following use cases: * Get the list of the todo items * Create a new todo item * Delete an existing todo item ### Application Service Interface We can start by defining an interface for the application service. Create a new `ITodoAppService` interface in the *TodoApp.Application.Contracts* project, as shown below: ````csharp using System; using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp.Application.Services; namespace TodoApp { public interface ITodoAppService : IApplicationService { Task> GetListAsync(); Task CreateAsync(string text); Task DeleteAsync(Guid id); } } ```` ### Data Transfer Object `GetListAsync` and `CreateAsync` methods return `TodoItemDto`. `ApplicationService` typically gets and returns DTOs ([Data Transfer Objects](../../Data-Transfer-Objects.md)) instead of entities. So, we should define the DTO class here. Create a new `TodoItemDto` class inside the *TodoApp.Application.Contracts* project: ````csharp using System; namespace TodoApp { public class TodoItemDto { public Guid Id { get; set; } public string Text { get; set; } } } ```` This is a very simple DTO class that matches our `TodoItem` entity. We are ready to implement the `ITodoAppService`. ### Application Service Implementation Create a `TodoAppService` class inside the *TodoApp.Application* project, as shown below: ````csharp using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Volo.Abp.Application.Services; using Volo.Abp.Domain.Repositories; namespace TodoApp { 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 was defined before. 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) > **We will keep the UI side minimal for this tutorial to make the tutorial simple and focused. See the [web application development tutorial](../Part-1.md) to build real-life pages with all aspects.** {{if UI=="MVC"}} ### Index.cshtml.cs Open the `Index.cshtml.cs` file in the `Pages` folder of the *TodoApp.Web* project and replace the content with the following code block: ````csharp using System.Collections.Generic; using System.Threading.Tasks; namespace TodoApp.Web.Pages { public class IndexModel : TodoAppPageModel { 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 of the *TodoApp.Web* project and replace it with the following content: ````xml @page @model TodoApp.Web.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 Open the `Index.js` file in the `Pages` folder of the *TodoApp.Web* project and replace it with the following content: ````js $(function () { // DELETING ITEMS ///////////////////////////////////////// $('#TodoList').on('click', 'li i', function(){ var $li = $(this).parent(); var id = $li.attr('data-id'); todoApp.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.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* section to understand how it works. But now, let's continue and complete the application. ### Index.css As the final touch, open the `Index.css` file in the `Pages` folder of the *TodoApp.Web* project 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 and see the result. ### Dynamic JavaScript Proxies & Auto API Controllers In the `Index.js` file, we've used the `todoApp.todo.delete(...)` and `todoApp.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. 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=="Blazor" || UI=="BlazorServer"}} ### Index.razor.cs Open the `Index.razor.cs` file in the `Pages` folder of the *TodoApp.Blazor* project and replace the content with the following code block: ````csharp using Microsoft.AspNetCore.Components; using System.Collections.Generic; using System.Threading.Tasks; namespace TodoApp.Blazor.Pages { public partial class Index { [Inject] private ITodoAppService 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 `ITodoAppService` to perform operations for the 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. {{if UI=="Blazor"}} See the *Dynamic C# Proxies & Auto API Controllers* section below to learn how we could inject and use the application service interface from the Blazor application which is running on the browser! But now, let's continue and complete the application. {{end # Blazor}} ### Index.razor Open the `Index.razor` file in the `Pages` folder of the *TodoApp.Blazor* project 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 of the *TodoApp.Blazor* project 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. {{if UI=="Blazor"}} ### Dynamic C# Proxies & Auto API Controllers In the `Index.razor.cs` file, we've injected (with the `[Inject]` attribute) and used the `ITodoAppService` just like using a local service. Remember that the Blazor application is running on the browser while the implementation of this application service is running on the server. The magic is done by the ABP Framework's [Dynamic C# Client Proxy](../../API/Dynamic-CSharp-API-Clients.md) system. It uses the standard `HttpClient` and performs HTTP API requests to the remote server. It also handles all the standard tasks for us, including authorization, JSON serialization and exception handling. However, you may ask that we haven't created any API Controller, 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 run the `TodoApp.HttpApi.Host` application, you can see the Todo API: ![todo-api](todo-api.png) {{end # Blazor}} {{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.HttpApi.Host` project since the proxy generator reads API definitions from the server application. > **Warning**: There is an issue with IIS Express: it doesn't allow connecting to the application from another process. If you are using Visual Studio, select the `TodoApp.HttpApi.Host` instead of IIS Express in the run button drop-down list, as shown in the figure below: ![run-without-iisexpress](run-without-iisexpress.png) Once you run the `TodoApp.HttpApi.Host` project, open a command-line terminal in the `angular` folder and type 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 (170978 bytes) CREATE src/app/proxy/README.md (1000 bytes) CREATE src/app/proxy/todo.service.ts (794 bytes) CREATE src/app/proxy/models.ts (66 bytes) CREATE src/app/proxy/index.ts (58 bytes) ```` We can then use `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: ````js import { ToasterService } from '@abp/ng.theme.shared'; import { Component, OnInit } from '@angular/core'; import { TodoItemDto, TodoService } from '@proxy'; @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 In this tutorial, we've built a very simple application to warm up for the ABP Framework. If you are looking to build a serious application, please check the [web application development tutorial](../Part-1.md) which covers all the aspects of real-life web application development. ## 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)