Building an Angular CRUD application step by step

Dayana Jabif
Learn Angular
Published in
18 min readMar 5, 2018
https://angular-templates.io/tutorials/about/learn-angular-from-scratch-step-by-step

This tutorial takes you through the steps of creating an Angular application with TypeScript.

In the first part of this angular tutorial we explained the core concepts of Angular Framework and how to set your development environment to build Angular applications.

This example app aims to help you learn the fundamental concepts of Angular Framework. With a questions and answers format (Q&A), the users will be able to make questions about different Angular key concepts and answer those from others. It will have a listing of some Angular key concepts such as Angular CLI and Typescript and within each of these categories, a questions list with the corresponding answers, the option to vote them (up-vote, down-vote) and some forms to create/delete/update questions and answers.

The plan for this tutorial is to build an app that takes you step-by-step from setup to a full-featured example that serves to demonstrate the essential characteristics of a professional application: a sensible project structure, data binding, services, resolvers, pipes, angular material, dependency injection, navigation and remote data access.

We will learn enough core Angular to get started and gain the confidence that Angular can do whatever we need it to do. We will be covering a lot of ground at an introductory level but we will also link to plenty of references to topics with greater depth.

The app consists in a CRUD (Create, Read, Update, Delete) of Questions and Answers where people can post new questions and answer other people’s questions.

The App will have the following functionalities:

  • Manage Questions (Create, Delete)
  • Manage Answers (Create, Update, Delete)
  • List all the Questions form a category in a list format
  • List all the Answers from a particular Question in a list format
  • Enable people to vote Questions and Answers (up-votes and down-votes)

This is how the final App will look like:

Questions list with up-vote and down-vote functionalities
Modal to create a new answer

In this first part, you will learn how to:

  • Create classes to represent objects from the model
  • Create services to create, update and remove objects
  • Create pages and components to represent the functionalities and display the user interface

Angular apps architecture

Angular is a framework designed to build single page applications (SPAs) and most of its architecture design is focused towards doing that in an effective manner.

Single-page application (or SPA) are applications that are accessed via web browser like other websites but offer more dynamic interactions resembling native mobile and desktop apps. The most notable difference between a regular website and SPA is the reduced amount of page refreshes.

Typically, 95 percent of SPA code runs in the browser; the rest works in the server when the user needs new data or must perform secured operations such as authentication.

As a result, the process of page rendering happens mostly on the client-side.

Angular Modules

Modules help organize an application into cohesive functionality blocks by wrapping components, pipes, directives, and services. They are just all about developer ergonomics.

Angular applications are modular. Every Angular application has at least one module — the root module, conventionally named AppModule. The root module can be the only module in a small application, but most apps have many more modules. As the developer, it’s up to you to decide how to use the modules. Typically, you map major functionality or a feature to a module. Let’s say you have four major areas in your system. Each one will have its own module in addition to the root module, for a total of five modules.

Any angular module is a class with the @NgModule decorator. Decorators are functions that modify JavaScript classes. They are basically used for attaching metadata to classes so that it knows the configuration of those classes and how they should work. The @NgModule decorator properties that describe the module are:

  • declarations: The classes that belong to this module and are related to views. There are three classes in Angular that can contain views: components, directives and pipes.
  • exports: The classes that should be accessible to other modules components.
  • imports: Modules whose classes are needed by the components of this module.
  • providers: Services present in one of the modules which are going to be used in the other modules or components. Once a service is included in the providers, it becomes accessible in all parts of that application.
  • bootstrap: The root component which is the main view of the application. Only the root module has this property and it indicates the component that’s gonna be bootstrapped.
  • entryComponents: An entry component is any component that Angular loads imperatively, (which means you’re not referencing it in the template), by type.

Angular Components

Components are the most basic building block of an UI and Angular applications. A component controls one or more sections on the screen (what we call views). For example in this example we have components like AppComponent (the bootstrapped component), CategoriesComponent, CategoryQuestionsComponent, QuestionAnswersComponent etc.

A component is self contained and represents a reusable piece of UI that is usually constituted by three important things:

  • A piece of html code that is known as the view
  • A class that encapsulates all available data and interactions to that view through an API of properties and methods architectured by Angular. Here’s where we define the application logic (what it does to support the view)
  • And the aforementioned html element also known as selector.

Using the Angular @Component decorator we provide additional metadata that determines how the component should be processed, instantiated and used at runtime. For example we set the html template related to the view, then, we set the html selector that we are going to use for that component, we set stylesheets for that component.

The Component passes data to the view using a process called Data Binding. This is done by Binding the DOM Elements to component properties. Binding can be used to display property values to the user, change element styles, respond to an user event, etc.

A component must belong to an NgModule in order for it to be usable by another component or application. To specify that a component is a member of an NgModule, you should list it in the declarations property of that NgModule.

One side note on the components importance from a point of software architecture principles:

It’s super important and recommended to have separate components, and this is why

Imagine we have two different UI blocks in the same component and in the same file. At the beginning, they may be small but each could grow. We are sure to receive new requirements for one and not the other. Yet every change puts both components at risk and doubles the testing burden without any benefits. If we had to reuse some of those UI blocks elsewhere in our app, the other one would be glued along.

That scenario violates the Single Responsibility Principle. You may think this is only a tutorial, but we need to do things right — especially if doing them right is easy and we learn how to build Angular apps in the process.

Angular encourages this principle by having each patch of the page controlled with it’s own component.

A typical Angular application looks like a tree of components. The following diagram illustrates this concept. Note that the modal components are on the side of the parent component because they are imperative components which are not declared on the component html template.

Angular app tree of components

Angular building blocks: Templates

Templates are used to define a component view. A template looks like regular HTML, but it also has some differences. Code like *ngFor, {{hero.name}}, (click), and [hero] uses Angular template syntax to enhance HTML markup capabilities. Templates can also include custom components like <custom-element> in the form of non-regular html tags. These components mix seamlessly with native HTML in the same layouts.

Angular building blocks: Services

Almost anything can be a service, any value, function, or feature that your application needs. A service is typically a class with a narrow, well-defined purpose. It should do something specific and do it well. The main purpose of Angular Services is sharing resources across components.

Take Component classes, they should be lean, component’s job is to enable the user experience (mediate between the view and the application logic) and nothing more. They don’t fetch data from the server, validate user input, or log directly to the console. They delegate such tasks and everything nontrivial to services.

Services are fundamental to any Angular application, and components are big consumers of services as they help them being lean.

The scenario we’ve just described has a lot to do with the Separation of Concerns principle. Angular doesn’t enforce these principles, but it helps you follow these principles by making it easy to structure your application logic into services and make those services available to components through dependency injection.

In our example app we have three services: AnswersService, QuestionsService, CategoriesService. Each of them has only the functions related to them. In this specific tutorial we will only focus on CategoriesService and in the following parts we will discuss the others.

CategoriesService has the following methods:

//gets all the question categories from a local json
getCategories(){
return this.http.get("./assets/categories.json")
.map((res:any) => res.json())
.toPromise();
}

//finds a specific category by slug
getCategoryBySlug(slug: string){
return this.getCategories()
.then(data =>{
return data.categories.find((category) => {
return category.slug == slug;
});
})
}

Angular building blocks: Other resources

External resources like Databases, API’s, etc, are fundamental as they will enable our app to interact with the outside world.

There’s much more to cover about the basic building blocks of Angular applications like Dependency Injection, Data Binding, Directives, etc. You can find these and much more information in our upcoming post about “Angular: The learning path” from our blog in AngularTemplates.

Now, let’s go deeper and map the project structure to the app’s architecture so we can understand better how all the pieces interact with each other.

If you want to see a solid angular application with Universal (server-side rendering), ahead-of-time compilation, lazy loading, angular material and lots of beautiful and useful components working together nicely you can check our most recent creation:

Angular example app project structure

After following the setup instructions for creating a new project in the previous section, let’s walk through the anatomy of our Angular 5 app. The CLI setup procedures install lots of different files. Most of them can be safely ignored.

In the project root we have three important folders and some important files:

  • /src/ — This is the most important folder. Here we have all the files that make our Angular app.
  • /e2e/ — This folder is for the End-to-end tests of the application, written in Jasmine and run by the protractor e2e test runner. Please note that we will not enter in details about testing in this post.
  • nodemodules/ — The npm packages installed in the project with the npm install command.
  • package.json — As every modern web application, we need a package system and package manager to handle all the third-party libraries and modules our app uses. Inside this file, you will find all the dependencies and some other handy stuff like the npm scripts that will help us a lot to orchestrate the development (bundling/compiling) workflow.
  • tsconfig.json — Main configuration file. It needs to be in the root path as it’s where the typescript compiler will look for it.
  • webpack.config.js — Main configuration file, it needs to be in the root path as it’s where the typescript compiler will look for it.

Inside of the /src directory we find our raw, uncompiled code. This is where most of the work for your Angular app will take place. When we run ng serve, our code inside of /src gets bundled and transpiled into the correct Javascript version that the browser understands (currently, ES5). That means we can work at a higher level using TypeScript, but compile down to the older form of Javascript that the browser needs.

Under this folder you will find two main folder structures.

  • /app — has all the components, modules, pages you will build the app upon.
  • /environments — this folder is to manage the different environment variables such as dev and prod. For example we could have a local database for our development environment and a product database for production environment. When we run ng serve it will use by default the dev env. If you like to run in production mode you need to add the --prod flag to the ng serve.
  • index.html/ — It’s the app host page but you won’t be modifying this file often, as in our case it only serves as a placeholder. All the scripts and styles needed to make the app work are gonna be injected automatically by the webpack bundling process, so you don’t have to do this manually. The only thing that comes to my mind now, that you may include in this file, are some meta tags (but you can also handle these through Angular as well).

And there are other secondary but also important folders

  • /assets — in this folder you will find images, sample-data json’s, and any other asset you may require in your app.

Angular best practices: The app folder

This is the core of the project. Let’s have a look at the structure of this folder so you get an idea where to find things and where to add your own modules to adapt this project to your particular needs.

/shared

The SharedModule that lives in this folder exists to hold the common components, directives, and pipes and share them with the modules that need them.

It imports the CommonModule because its component needs common directives. You will notice that it re-exports other modules. If you review the application, you may notice that many components requiring SharedModule directives also use NgIf and NgFor from CommonModule and bind to component properties with [(ngModel)], a directive in the FormsModule. Modules that declare these components would have to import CommonModule, FormsModule, and SharedModule.

You can reduce repetition by having SharedModule re-export CommonModule and FormsModule so that importers of SharedModule get CommonModule and FormsModule for free. SharedModule can still export FormsModule without listing it among its imports.

/styles

Here you will find all the variables, mixins, shared styles, etc, that will make your app customizable and extendable.

Maybe you don’t know Sass? Briefly, it is a superset of css that will ease and speed your development cycles incredibly.

/services

Here you will find all the services needed in this app. Each service has only the functions related to it.

Other folders

To gain in code modularity, we’ve created a folder for each component. Within those folders you will find every related file for the pages included in that component. This includes the html for the layout, sass for the styles and the main page component.

app.component.html

This serves as the skeleton of the app. Typically has a <router-outlet> to render the routes and their content. It can also be wrapped with content that you want to be in every page (for example a toolbar).

app.component.ts

It’s the Angular component that provides functionality to the app.component.html file I just mentioned about.

app.routes.ts

Here we define the main routes. These routes are registered to the Angular RouterModule in the AppModule. If you use lazy modules, child routes of other lazy modules are defined inside those modules.

app.module.ts

This is the main module of the project which will bootstrap the app.

As we advance in this tutorial we will be creating more pages and perform basic navigation.

Angular app navigation and routing

After seeing the components diagram and the project structure, this is the navigation we propose for the app. We start in the categories page and from there we can only navigate to the questions of one of the categories. Then follow the arrows from the image below to see the other navigations available in this angular 5 example app.

Note that the modals for creating, updating and deleting are not shown in this image because they don’t represent an app navigation. Another important thing to consider is that we used Breadcrumbs to navigate back to the previous pages.

Our Angular 5 app navigation

Before we start thinking about navigation, we must consider the type and amount of data you want to display in your app. Don’t forget you will use navigation to show and structure your data, that is why it should follow the information structure of your app and not the other way around.

It is important to keep the best practices for navigation design. This ensures that people will be able to use and find the most valuable features in your app.

Good navigation, like good design, is invisible.

You can find more information about the details of UI/UX of navigation in our post about mobile ui design navigation principles.

If you are looking for a more complex navigation with side menus, tabs, and many other advanced configurations you should definitely check Angular Admin Template

Angular Admin Template available in https://angular-templates.io/

App’s Navigation using Angular Router Module

A little more about the navigation

Angular has a specific module dedicated to navigation and routing, the RouterModule. With this module you can create routes, which allows you to move from one part of the application to another part or from one view to another.

For routes to work, you need an anchor or element in the UI to map actions (typically clicks on elements) to routes (URL paths). We use the routerLink directive for this purpose. For example, when the user clicks on a Category name in the UI, Angular, through the routerLink directive, knows that it needs to navigate to the following url: http://localhost:4200/questions/about/category-name

<a class="list-title" [routerLink]="['/questions/about', category.slug]">{{category.title}}</a>

Next, you’ll need to map the URL paths to the components. In the same folder as the root module, create a config file called app.routes.ts (if you don’t have one already) with the following code.

import { Routes } from '@angular/router';

export const routes: Routes = [
{
path: '',
component: CategoriesComponent,
resolve: {
data: CategoriesResolver
}
},
{
path: 'questions/about/:categorySlug',
component: CategoryQuestionsComponent,
resolve: {
data: CategoryQuestionsResolver
}
},
{
path: 'question/:questionSlug',
component: QuestionAnswersComponent,
resolve: {
data: QuestionAnswersResolver
}
}
];

For each route we provide a path (also known as the URL) and the component that should be rendered at that path. The empty string for the CategoriesComponent's path means that the CategoriesComponent will be rendered when there is no URL (also known as the root path).

Note that for each route we also have a resolve. Using a resolve in our navigation routes allows us to pre-fetch the component’s data before the route is activated. Using resolves is a very good practice to make sure that all necessary data is ready for our components to use and avoid displaying a blank component while waiting for the data.

For example in we use a CategoriesResolver to fetch the list of categories. Once the categories are ready, we activate the route. Please note that if the resolve Observable does not complete, the navigation will not continue.

Finally, the root module must also know the routes we defined above. Add a reference to the routes in the imports property of the AppModule.

import { routes } from './app.routes';

imports: [
RouterModule.forRoot(routes,
{ useHash: false }
)
],

Notice how we use forRoot (or eventually forChild) methods on the RouterModule (the docs explain the difference in detail, but for now just know that forRoot should only be called once in your app for top level routes).

Angular Material Design Components to enhance UI/UX

Angular Material 2 vs ngx-bootstrap

There are some libraries that provide high-level components which allow you to quickly construct a nice interface for your app. These include modals, popups, cards, lists, menus, etc. They are reusable UI elements that serve as the building blocks for your mobile app, made up of HTML, CSS, and sometimes JavaScript.

Two of the most used UI component libraries are Angular Material and ngx-bootstrap. Angular Material is the official Angular UI library and provides tons of components.

On the other hand, ngx-bootstrap provides a series of Angular components made on top of Twitter Bootstrap framework.

In this Angular tutorial we are going to use Angular Material, but feel free to choose the one that best fits your needs as they are both super complete and robust.

Please feel free to dig the library of UI components that Angular Material has in their components documentation page.

Adding a backend to our Angular example project

Different alternatives for backend API data integrations

The key to an evolving app is to create reusable services to manage all the data calls to your backend.

As you may know, there are many ways when it comes to data handling and backend implementations. In this tutorial we will explain how to consume data from a static json file with dummy data. In the next tutorial Learn how to build a MEAN stack application you will learn how to build and consume data from a REST API with Loopback (a node.js framework perfectly suited for REST API’s) and MongoDB (to store the data).

Both implementations (static json and remote backend API) need to worry about the app’s side of the problem, how to handle data calls. This works the same and is independent on the way you implement the backend. We will talk about models and services and how they work together to achieve this.

We encourage the usage of models in combination with services for handling data all the way from the backend to the presentation flow.

Domain Models

Domain models are important for defining and enforcing business logic in applications and are especially relevant as apps become larger and more people work on them. At the same time, it is important that we keep our applications DRY and maintainable by moving logic out of components themselves and into separate classes (models) that can be called upon. A modular approach such as this, makes our app’s business logic reusable. To learn more about this, please visit this great post about angular 2 domain models.

Data Services

Angular enables you to create multiple reusable data services and inject them in the components that need them. Refactoring data access to a separate service, keeps the component lean and focused on supporting the view. It also makes it easier to unit test the component with a mock service. To learn more about this, please visit angular 2 documentation about services.

In our case, we defined a model for the question categories data we are pulling from the static json file. This model is used by the categories.service.ts.

//in category.model.ts
export class CategoryModel {
slug: string;
title: string;
image: string;
description: string;
tags: Array<Object>;
}

//in categories.service.ts
getCategories(): Promise<CategoryModel[]> {
return this.http.get("./assets/categories.json")
.toPromise()
.then(res => res.json() as CategoryModel[])
}

And we use this service in the categories.resolver.ts where we fetch the categories view data.

//in categories.resolver.ts
import { Injectable } from '@angular/core';
import { Resolve } from "@angular/router";
import { CategoriesService } from "../services/categories.service";

@Injectable()
export class CategoriesResolver implements Resolve<any> {

constructor(private categoriesService: CategoriesService) { }

resolve() {
return new Promise((resolve, reject) => {

let breadcrumbs = [
{ url: '/', label: 'Categories' }
];

//get categories from local json file
this.categoriesService.getCategories()
.then(
categories => {
return resolve({
categories: categories,
breadcrumbs: breadcrumbs
});
},
err => {
return resolve(null);
}
)
});
}
}

Each time we add a new service remember that the Angular injector does not know how to create that Service by default. If we ran our code now, Angular would fail with an error. After creating services, we have to teach the Angular injector how to make that Service by registering a Service provider.

According to the Angular documentation page for dependency injection there are two ways to register the Service provider: in the Component itself or in the Module (NgModule). In our case, we register all services in the app.module.ts

//in app.module.ts
@NgModule({
declarations: [
AppComponent,
CategoriesComponent,
CategoryQuestionsComponent,
NewQuestionModalComponent,
NewAnswerModalComponent,
UpdateAnswerModalComponent,
QuestionAnswersComponent,
DeleteQuestionModalComponent,
DeleteAnswerModalComponent
],
imports: [
RouterModule.forRoot(routes,
{ useHash: false }
),
SharedModule
],
entryComponents: [

],
providers: [
CategoriesService,
QuestionsService,
AnswersService,
CategoryQuestionsResolver,
CategoriesResolver,
QuestionAnswersResolver
],
bootstrap: [AppComponent]
})

export class AppModule { }

One side note on the importance of Dependency Injection from the software architecture principles point: Remember we just mentioned that we “inject” data services in the components that need them? Well, this concept is called Dependency Injection and it is super important to know more about this. Do we new() the Services? No way!

That’s a bad idea for several reasons including:

  • Our component has to know how to create the Service. If we ever change the Service constructor, we will have to find every place we create the service and fix it. Running around patching code is error prone and adds to the test burden.
  • We create a new service each time we use new. What if the service should cache results and share that cache with others? We couldn’t do that.
  • We are locking the Component (where we new the service) into a specific implementation of the Service. It will be hard to switch implementations for different scenarios. Can we operate offline? Will we need different mocked versions under test? Not easy.

We get it. Really we do. But it is so ridiculously easy to avoid these problems that there is no excuse for doing it wrong.

Next: Advanced Angular example

Final thoughts and next steps

In this angular step by step tutorial we went from the basic concepts and why’s of Angular Framework to building a complete Angular 5 app using Angular Material components. We explained one by one the main building blocks of an Angular application as well as the best practices for building a complete app with Angular. Also this tutorial shows how to setup your dev environment so you can start developing Angular apps in your computer right now.

In this tutorial we only explained the first part of the code example which is the categories list fetched from a static json file. In our next tutorials we will explore deeper into the details of implementing the remote backend API using Loopback and how to deploy it in a remote server.

Hopefully, you didn’t run into any issues with this Learn Angular from scratch step by step tutorial, but if you did, feel free to leave a comment below. Remember you can get the source code of this Angular 5 app.

Originally published at angular-templates.io.

--

--

Dayana Jabif
Learn Angular

Driven by living a free & happy life. I create #angular & #ionic resources to help devs build better apps faster 🚀. Founder of @ionicthemes & @ng_templates