Version
Language

There are multiple versions of this document. Pick the options that suit you best.

UI
Database

Quick Start

This is a single-part, quick-start tutorial to build a simple todo application with the ABP Framework. Here, a screenshot from the final application:

todo-list

You can find source code of the completed application here.

Pre-Requirements

Creating a New Solution

We will use the ABP CLI to create new solutions with the ABP Framework. You can run the following command in a command-line terminal to install it:

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:

abp new TodoApp -u blazor -d mongodb

This will create a new solution, named TodoApp. Once the solution is ready, open it in your favorite IDE.

Create the Database

If you are using Visual Studio, right click to 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.

Run the Application

It is good to run the application before starting the development. The solution has two main applications;

  • TodoApp.HttpApi.Host host 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:

todo-swagger-ui-initial

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:

todo-ui-initial

You can click to the Login button, use admin as the username and 1q2w3E* as the password to login to the application.

All ready. We can start the coding!

Domain Layer

This application has a single entity and we are starting by creating it. Create a new TodoItem class inside the TodoApp.Domain project:

using System;
using Volo.Abp.Domain.Entities;

namespace TodoApp
{
    public class TodoItem : BasicAggregateRoot<Guid>
    {
        public string Text { get; set; }
    }
}

BasicAggregateRoot is one the simplest base class to create root entities, and Guid is the primary key (Id) of the entity here.

Database Integration

Next step is to setup the MongoDB 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:
public IMongoCollection<TodoItem> TodoItems => Collection<TodoItem>();
  1. Add the following code inside the CreateModel method:
modelBuilder.Entity<TodoItem>(b =>
{
    b.CollectionName = "TodoItems";
});

Now, we can use ABP repositories to save and retrieve todo items, as we'll do in the next section.

Application Layer

An Application Service is used to perform use cases of the application. We need to perform the following use cases;

  • Get the list of 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:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;

namespace TodoApp
{
    public interface ITodoAppService : IApplicationService
    {
        Task<List<TodoItemDto>> GetListAsync();
        Task<TodoItemDto> CreateAsync(string text);
        Task DeleteAsync(Guid id);
    }
}

Data Transfer Object

GetListAsync and CreateAsync methods return TodoItemDto. Applications Services typically gets and returns DTOs (Data Transfer Objects) instead of entities. So, we should define the DTO class here. Create a new TodoItemDto class inside the TodoApp.Application.Contracts project:

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 to 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:

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<TodoItem, Guid> _todoItemRepository;

        public TodoAppService(IRepository<TodoItem, Guid> 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 for the entities. We can use them to perform the fundamental database operations. This class injects IRepository<TodoItem, Guid>, 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:

public async Task<List<TodoItemDto>> 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 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:

public async Task<TodoItemDto> CreateAsync(string text)
{
    var todoItem = await _todoItemRepository.InsertAsync(
        new TodoItem {Text = text}
    );

    return new TodoItemDto
    {
        Id = todoItem.Id,
        Text = todoItem.Text
    };
}

Repository's InsertAsync method inserts the given TodoItem to 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:

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, a sample screenshot from the final UI:

todo-list

We will keep the UI side minimal for this tutorial to make the tutorial simple and focused. See the web application development tutorial to build real-life pages with all aspects.

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:

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<TodoItemDto> TodoItems { get; set; } = new List<TodoItemDto>();
        private string NewTodoText { get; set; }

        protected async override 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 ITodoAppService to perform operations for the todo items. It manipulates the TodoItems list after create and delete operations. In this way, we don't need to refresh the whole todo list from the server.

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.

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:

@page "/"
@inherits TodoAppComponentBase
<div class="container">
    <Card>
        <CardHeader>
            <CardTitle>
                TODO LIST
            </CardTitle>
        </CardHeader>
        <CardBody>
            <!-- FORM FOR NEW TODO ITEMS -->
            <form id="NewItemForm" 
                  @onsubmit:preventDefault
                  @onsubmit="() => Create()"
                  class="form-inline">
                <input type="text" 
                       @bind-value="@NewTodoText"
                       class="form-control mr-2" 
                       placeholder="enter text...">
                <button type="submit" class="btn btn-primary">Submit</button>
            </form>

            <!-- TODO ITEMS LIST -->
            <ul id="TodoList">
                @foreach (var todoItem in TodoItems)
                {
                    <li data-id="@todoItem.Id">
                        <i class="far fa-trash-alt"
                           @onclick="() => Delete(todoItem)"
                           ></i> @todoItem.Text
                    </li>
                }
            </ul>
        </CardBody>
    </Card>
</div>

Index.razor.css

As the final touch, open the Index.razor.css file in the Pages folder of the TodoApp.Web project and replace with the following content:

#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.

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 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 server handles these requests? This question brings us the Auto API Controller feature of the ABP Framework. It automatically converts the application services to API Controllers by conventions.

If you run the TodoApp.HttpApi.Host application, you can see the Todo API:

todo-api

Conclusion

In this tutorial, we've build a very simple application to warm up to the ABP Framework. If you are looking to build a serious application, please check the web application development tutorial which covers all the aspects of a real-life web application development.

Source Code

You can find source code of the completed application here.

See Also

Was this page helpful?
Please make a selection.
Thank you for your valuable feedback!

Please note that although we cannot respond to feedback, our team will use your comments to improve the experience.

In this document
Mastering ABP Framework Book
Mastering ABP Framework

This book will help you gain a complete understanding of the framework and modern web application development techniques.