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:
You can find source code of the completed application here.
Pre-Requirements
An IDE (e.g. Visual Studio) that supports .NET 5.0+ development.
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 angular
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.
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.
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 thedotnet run
command.
Run the Application
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 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:
You can explore and test your HTTP API with this UI. If that works, we can run the Angular client application.
First, run the following command to restore the NPM packages;
npm install
It will take some time to install all the packages. Then you can run the application using the following command:
npm start
This command takes time, but eventually runs and opens the application in your default browser:
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 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 Entity Framework Core 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:
public DbSet<TodoItem> TodoItems { get; set; }
Then locate to OnModelCreating
method in the TodoAppDbContext
class and add the mapping code for the TodoItem
entity:
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<TodoItem>(b =>
{
b.ToTable("TodoItems");
});
}
We've mapped TodoItem
entity to a TodoItems
table in the database.
Code First Migrations
The startup solution is configured to use Entity Framework Core Code First 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:
dotnet ef migrations add Added_TodoItem
This will add a new migration class to the project:
You can apply changes to the database using the following command, in the same command-line terminal:
dotnet ef database update
If you are using Visual Studio, you may want to use
Add-Migration Added_TodoItem
andUpdate-Database
commands in the Package Manager Console (PMC). In this case, ensure thatTodoApp.HttpApi.Host
is the startup project andTodoApp.EntityFrameworkCore
is the Default Project in PMC.
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
. ApplicationService
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:
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.
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 to connect 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:
Once you run the TodoApp.HttpApi.Host
project, open a command-line terminal in the angular
folder and type the following command:
abp generate-proxy
If everything goes well, it should generate an output like shown below:
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 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:
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 the 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 in 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:
<div class="container">
<div class="card">
<div class="card-header">
<div class="card-title">TODO LIST</div>
</div>
<div class="card-body">
<!-- FORM FOR NEW TODO ITEMS -->
<form class="form-inline" (ngSubmit)="create()">
<input
name="NewTodoText"
type="text"
[(ngModel)]="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">
<li *ngFor="let todoItem of todoItems">
<i class="fa fa-trash-o" (click)="delete(todoItem.id)"></i> {{ todoItem.text }}
</li>
</ul>
</div>
</div>
</div>
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:
#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.
Conclusion
In this tutorial, we've built 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.