Build real web app using .NET Core, Angular 5 and Template Stack templates (Part 2 - implement Todo app)

  This article is continuation of Part 1 - template review where we discussed how to download, sepup and use the Template Stack templates. Briefly the template that we are using has already implemented good architecture, database modifications, backend Token Authentication, cliend side login and regisred. The Angular is written in TypeScript and it's build using gulp.
  Now we want to implement the fallowing functionalities:
  • Adding Todo tasks, only by registered users
  • Listing of all user's tasks
  • Marking tasks as done
In order to implement the desired functionalities we have to do fallowing tasks
  • Backend-wise
  • Add new database table for the Todo tasks
  • Update the database with Code First Migration
  • Add new controller with a few API endpoints for adding, listing, or updating tasks (or so called CRUD operations)
  • Frontend-wise
  • Create new module, routing, and components for the Todo tasks
  • Create new service for the API calls
  • Add the new module and routing to the main modules
  • 1. Adding new table

    Adding new database table is as simple as adding new class and declaring it in the ApplicationDbContext:
    
     public class TodoItem : BaseDeletableModel<int>
        {
            [Required]
            public string Title { get; set; }
    
            public bool IsDone { get; set; }
    
            [Required]
            public string AuthorId { get; set; }
    
            public virtual ApplicationUser Author { get; set; }
        }
    
    
    The task has title, state (IsDone), and author, also it has the common properties (IsDeleted, DeletedOn, CreatedOn, ModifiedOn) shared between the classes due to the inherintance of BaseDeletableModel<int>
    In the ApplicationDbContext add collection of TodoItem
    
    
    public DbSet TodoItems { get; set; }
    
    
      Once you add this you can add code first migration from Tools -> NuGet Package Manager -> Package Manger Console. When you select this a window appear in which you have to type in the command:
    Add-Migration AddNewTableForTodoItems
    The first part is mandatory and the second is the name of the migration (which is by your choice). After a few seconds the new migration is scaffolded and you are ready to go with another command: Update-Database This is enough. At this point you will have your new table in the database. To be sure that everything went OK you can check your actual database if you have Management Studio.

    2. Add controller with CRUD operations

    
     public class TodoItemsController : BaseController
        {
            private readonly IDeletableEntityRepository repository;
    
            public TodoItemsController(IDeletableEntityRepository repository)
            {
                this.repository = repository;
            }
    
            [HttpGet]
            public IActionResult All()
            {
                var userId = this.User.GetId();
                var todoItems = this.repository.All().Where(t => t.AuthorId == userId).To().ToList();
    
                return this.Ok(todoItems);
            }
    
            [HttpPost]
            public async Task Create([FromBody]TodoItemBindingModel model)
            {
                if (model == null || !this.ModelState.IsValid)
                {
                    return this.BadRequest(this.ModelState.GetFirstError());
                }
    
                var todoItem = Mapper.Map(model);
                todoItem.AuthorId = this.User.GetId();
    
                this.repository.Add(todoItem);
                await this.repository.SaveChangesAsync();
    
                return this.Ok(Mapper.Map(todoItem));
            }
    
            [HttpPost]
            public async Task MarkAsDone(int id)
            {
                var todoItem = await this.repository.GetByIdAsync(id);
    
                if (todoItem == null)
                {
                    return this.NotFound();
                }
    
                if (todoItem.AuthorId != this.User.GetId())
                {
                    return this.Forbid(JwtBearerDefaults.AuthenticationScheme);
                }
    
                if (!todoItem.IsDone)
                {
                    todoItem.IsDone = true;
    
                    this.repository.Update(todoItem);
                    await this.repository.SaveChangesAsync();
                }
    
                return this.Ok();
            }
        }
    
    
      As you can see we are using directly the IDeletableEntityRepository interface, which is one of the implementations of the repository pattern which we mentioned in Part 1 - template review. Another aproach can be to create a TodoService but for simplicity we are using directly the repository here.The repository is being instantiate through the .NET Core dependency injection, so we are provided with the implementation of the interface in the constructor of the controller.
      In this controller we are creating three endpoints: one for the getting all user's tasks, one for adding new user task, and one for marking task as done with few validations.

    3. Creating modules, routing and angular components

      Looking at our desired functionalities we are goint to need at leats two components: one for adding and one for listing of the tasks.
    Moreover we can include these components in one bigger module which we can call user module, because we can enhance user functionalities with some new features. So awe going to create a folder containing UserModule, UserRoutingModule and one UserComponent with will contain our router-outlet directive. Inside this folder we will put our todo items in a nested folder.
    ...
    user.module.ts
    
    import { NgModule } from '@angular/core';
    import { CommonModule } from '@angular/common';
    import { FormsModule } from '@angular/forms';
    import { UserRoutingModule } from './user.routes';
    import { USER_COMPONENTS } from './index'
    
    @NgModule({
        imports: [
            CommonModule,
            FormsModule,
            UserRoutingModule
        ],
    
        declarations: [USER_COMPONENTS]
    })
    
    export class UserModule { }
    
    user.routes.ts
    
    import { NgModule } from '@angular/core';
    import { Routes, RouterModule } from '@angular/router';
    
    import {
        TodoItemCreateComponent,
        TodoItemsComponent,
        UserComponent
    } from './index';
    
    import { AuthGuardService } from '../../services/index';
    
    const ACCOUNT_ROUTES: Routes = [
        {
            path: '',
            component: UserComponent,
            canActivate: [AuthGuardService],
            canActivateChild: [AuthGuardService],
            children: [
                { path: '', redirectTo: '/user/todo-items', pathMatch: 'full' },
                { path: 'todo-item-create', component: TodoItemCreateComponent },
                { path: 'todo-items', component: TodoItemsComponent }
            ]
        }
    ];
    
    @NgModule({
        imports: [RouterModule.forChild(ACCOUNT_ROUTES)],
        exports: [RouterModule]
    })
    
    
    export class UserRoutingModule { }
    
    user.component.ts and user.component.html
    As we said the UserComponent contains only the special router-outlet angular directive which manages in what part of the component the new routes will be included with their components.
    index.ts
    This file just combines and exports all the components from the module
    todo-item-create.component.ts
    
    import { Component } from '@angular/core';
    import { HttpErrorResponse } from '@angular/common/http';
    import { TodoItemsDataService, RouterService } from '../../../services/index';
    import { TodoItem } from '../../../domain/index';
    
    @Component({
        moduleId: module.id,
        selector: 'todo-item-create',
        templateUrl: 'todo-item-create.component.html'
    })
    
    export class TodoItemCreateComponent {
        constructor(private todoItemsDataService: TodoItemsDataService, private routerService: RouterService) { }
    
        public todoItem: TodoItem = new TodoItem();
        public errorMessage: string = null;
    
        public create(): void {
            this.todoItemsDataService.create(this.todoItem).subscribe(
                () => this.routerService.navigateByUrl('/user/todo-items'),
                (error: HttpErrorResponse) => this.errorMessage = error.error || 'Create TODO item failed.');
        }
    }
    
    
    In this component we can see the use of angular Injectable services, which help us to hide the more complex logic of sending http requests to the server and just handle the result of it. Besides that we see an object TodoItem which is just a rugular TypeScript class with a few properties in it.
    todo-item-create.component.html
    It's a regular html form, it is available on GitHub Repo here.
    todo-items.component.ts
    
    import { Component, OnInit } from '@angular/core';
    import { HttpErrorResponse } from '@angular/common/http';
    import { TodoItemsDataService } from '../../../services/index';
    import { TodoItem } from '../../../domain/index';
    
    @Component({
        moduleId: module.id,
        selector: 'todo-items',
        templateUrl: 'todo-items.component.html'
    })
    
    export class TodoItemsComponent implements OnInit {
        constructor(private todoItemsDataService: TodoItemsDataService) { }
    
        public todoItems: TodoItem[] = [];
        public errorMessage: string = null;
    
        ngOnInit() {
            this.todoItemsDataService.getAll().subscribe((data: TodoItem[]) => this.todoItems = data);
        }
    
        public markAsDone(todoItem: TodoItem): void {
            this.todoItemsDataService.markAsDone(todoItem.id).subscribe(
                () => {
                    this.errorMessage = null;
    
                    todoItem.isDone = true;
                },
                (error: HttpErrorResponse) => this.errorMessage = error.error || 'Mark TODO as done failed.');
        }
    }
    
    
    todo-items.component.html
    It's a regular html form, it is available on GitHub Repo here.

    4. Creating the TodoService (which we already used)

    todo-items-data.service.ts
    
    import { Injectable } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    
    import { Observable } from 'rxjs/Observable';
    
    import { TodoItem } from '../../domain/todo-item';
    
    @Injectable()
    export class TodoItemsDataService {
        private static readonly URLS = {
            ALL: 'api/todoitems/all',
            CREATE: 'api/todoitems/create',
            MARK_AS_DONE: 'api/todoitems/markasdone/'
        };
    
        constructor(private httpClient: HttpClient) { }
    
        public getAll(): Observable {
            return this.httpClient.get(TodoItemsDataService.URLS.ALL);
        }
    
        public create(todoItem: TodoItem): Observable {
            return this.httpClient.post(TodoItemsDataService.URLS.CREATE, todoItem);
        }
    
        public markAsDone(id: number): Observable {
            return this.httpClient.post(`${TodoItemsDataService.URLS.MARK_AS_DONE}${id}`, null);
        }
    }
    
    
      The service wraps the http requests logic in a couple of functions and returns Observable object to which we are able to subscribe and manage the successfull or error states of the query.

    5. Combining everything in working example

    Now that we have done our new modules and componenets we have to incorporate them in the whole solution. This is very common when we speak for Angular. In simple words all new components and modules have to be declared in the main module.
      One of the ways we can do that is to make it using the main routing
    app.routes.ts
    
    import { NgModule } from '@angular/core';
    import { Routes, RouterModule } from '@angular/router';
    
    import { HomeComponent } from './components/index';
    
    export const APP_ROUTES: Routes = [
        { path: '', component: HomeComponent, pathMatch: 'full' },
    
        { path: 'account', loadChildren: 'app/components/account/account.module#AccountModule' },
        { path: 'user', loadChildren: 'app/components/user/user.module#UserModule' }
    ];
    
    @NgModule({
        imports: [RouterModule.forRoot(APP_ROUTES)],
        exports: [RouterModule]
    })
    
    export class AppRoutingModule { }
    
    and then we can add the AppRoutingModule as import of the main app.module.ts. This is absolutely enough, and now we should be able to add tasks, list them and mark them as done.
    Another thing worth mentioning are the Guards services that we use to restrict on the client side certain routes. For example in user.routes.ts we are using AuthGuardService which simply checks if there is registered user, and if there isn't any it redirects to the login page. –ěn the contrary when you are accessing login and register routes there is guard that checks for not logged in users only. Of course all these checks are only client side and can be easily manipulated, so everrthing should be validated once again on the server. If you want to download a working example you should check here .
    The next part of this tutorial is how to upload this to a source control system for example GitHub. You can check it here.