How To Build a Weather App with Angular, Bootstrap, and the APIXU API

How To Build a Weather App with Angular, Bootstrap, and the APIXU API

<strong>In this tutorial, you'll create a weather app using Angular, Bootstrap, and the APIXU API. You'll be able to type a location into a search form and ...</strong>

In this tutorial, you'll create a weather app using Angular, Bootstrap, and the APIXU API. You'll be able to type a location into a search form and ...

Introduction

Angular is a front-end web framework built by Google. It allows developers to build single-page applications modeled around a model-view-controller (MVC) or model-view-viewmodel (MVVM) software architectural pattern. This architecture divides applications into different, but connected parts allowing for parallel development. Following this pattern, Angular splits its different components into the respective parts of a web application. Its components manage the data and logic that pertain to that component, display the data in its respective view, and adapts or controls the view based on the different messages that it receives from the rest of the app.

Bootstrap is a front-end library that helps developers build responsive websites (sites that adapt to different devices), quickly and effectively. It makes use of a grid system that divides each page into twelve columns, which ensures that the page maintains its correct size and scale no matter what device it’s being viewed on.

APIXU provides global weather data to users via their API. Using APIXU, a user can retrieve the latest weather as well as future weather forecasts for any location in the world.

In this tutorial, you’ll create a weather app using Angular, Bootstrap, and the APIXU API. You’ll be able to type a location into a search form and on submission of that form, see the current weather details for that location displayed in your app. The Angular version used in this tutorial is 7.2.0 and the Bootstrap version used is 4.2.1.

Prerequisites

Before you begin this tutorial, you’ll need the following:

Step 1 — Installing Angular

Before you begin creating your app, you need to install Angular. Open your terminal and run the following command to install the Angular CLI globally on your machine:

npm install -g @angular/cli

The Angular CLI is the Command Line Interface for Angular. It serves as the main way to create a new Angular project as well as the different sub-elements that make up an Angular project. Using the -g argument will install it globally.

After a short while, you’ll see the following output:

Output from installing Angular

...

You’ve now installed Angular on your local machine. Next, you’ll create your Angular application.

Step 2 — Creating Your Angular App

In this step you’ll create and configure your new Angular application, install all its dependencies, such as Bootstrap and jQuery, and then finally check that the default application is working as expected.

First, use the ng command to create an Angular application, you can run this from your terminal.

Note: If you’re on Windows, you may have issues trying to run an ng command from Command Prompt even though you’ve installed Node.js and npm correctly. For example, you may get an error such as: ng is not recognized as an internal or external command. In order to resolve this, please run the ng command inside the installed Node.js command prompt located in the Node.js folder on Windows.

The ng command is a prerequisite to running any action with Angular from the command line. For example, whether you’re building a new project, creating components, or creating tests, you prefix each desired functionality with the ng command. In this tutorial, you’ll want to create a new application; you’ll achieve this by executing the ng newcommand. The ng new command creates a new Angular application, imports the necessary libraries, and creates all the default code scaffolding that your application requires.

Begin by creating a new application, in this tutorial it will be called <span class="highlight">weather-app</span>, but you can change the name as you wish:

ng new weather-app

The ng new command will prompt you for additional information about features that you want to add to your new application.

OutputWould you like to add Angular routing? (y/N)

The Angular routing allows you to build single page applications with different views using the routes and components. Go ahead and type y or hit ENTER to accept the defaults.

OutputWhich stylesheet format would you like to use? (Use arrow keys)

Hit ENTER to accept the default CSS option.

The app will continue its creation process, and after a short time you’ll see the following message:

Output...
CREATE weather-app/e2e/src/app.e2e-spec.ts (623 bytes)
CREATE weather-app/e2e/src/app.po.ts (204 bytes)
...
Successfully initialized git.

Next, in your text editor, open the <span class="highlight">weather-app</span> folder.

Looking at the structure of your directory, you’ll see several different folders and files. You can read a full explanation of what all of these files do here, but for the purposes of this tutorial, these are the most important files to understand:

  • The package.json file. Located in the root <span class="highlight">weather-app</span> folder, it performs just like any other Node.js application, holding all the libraries your application will use, the name of your application, commands to run when testing, and so on. Primarily, this file holds details about external libraries that your Angular application needs in order to run properly.

  • The app.module.ts file. Located in the app folder within the <span class="highlight">weather-app</span>/src folder, this file tells Angular how to assemble your application and holds details about the components, modules, and providers in your application. You’ll already have an imported module, BrowserModule, within your imports array. The BrowserModule provides essential services and directives for your application and should always be the first imported module in your imports array.

  • The angular.json file. Located in the root <span class="highlight">weather-app</span> folder of your app, this is the configuration file for the Angular CLI. This file holds internal configuration settings of what your Angular application needs to run. It sets defaults for your entire application, and has options such as what configuration files to use when testing, what global styles to use in your app, or to which folder to output your build files. You can find out more about these options in the official Angular-CLI documentation.

You can leave all of these files alone for the moment, as you’ll install Bootstrap next.

Bootstrap has two dependencies that you’ll need to install in order for it to work properly in Angular — jQuery and popper.jsjQuery is a JavaScript library focused on client-side scripting, while popper.js is a positioning library that mainly manages tooltips and popovers.

In your terminal, move to your root <span class="highlight">weather-app</span> directory:

cd weather-app

Then execute the following command to install all of the dependencies and save the references to the package.jsonfile:

npm install --save jquery popper.js bootstrap

The --save option automatically imports your references into the package.json file so that you don’t have to manually add them after installation.

You’ll see output showing the version numbers that were installed, like the following:

Output+ [email protected]

You have now successfully installed Bootstrap and its dependencies. However, you’ll also need to include these libraries inside your application. Your <span class="highlight">weather-app</span> does not yet know that it’ll need these libraries, therefore you need to add the paths to jquerypopper.jsbootstrap.js, and bootstrap.css into your angular.json file.

For popper.js, the file you’ll need to include is node_modules/popper.js/dist/umd/popper.js. jQuery requires the node_modules/jquery/dist/jquery.slim.js file. Finally, for Bootstrap you’ll need two files (both the JavaScript file and the CSS file). These are node_modules/bootstrap/dist/js/bootstrap.js and node_modules/bootstrap/dist/css/bootstrap.css respectively.

Now that you have all the required file paths, open the angular.json file in your text editor. The styles array is where you’ll add the reference to the CSS files, whilst the scripts array will reference all the scripts. You’ll find both of these arrays near the top of the angular.json file, within the "options": JSON object. Add the following highlighted content to the file:

angular.json

...
"options:" {
...
"styles": [
"node_modules/bootstrap/dist/css/bootstrap.css",
"src/styles.css"
],
"scripts": [
"node_modules/jquery/dist/jquery.slim.js",
"node_modules/popper.js/dist/umd/popper.js",
"node_modules/bootstrap/dist/js/bootstrap.js"
]},
...

You’ve now imported the main .js and .css files you need for Bootstrap to work properly. You’ve specified the relative paths to these files from your angular.json file: adding your .css files in the styles array and .js files in the scripts array of angular.json. Make sure you’ve saved the angular.json file after adding this content.

Now, start your application with the ng serve command to check that everything is working correctly. From the <span class="highlight">weather-app</span> directory in your terminal, run:

ng serve --o

The --o argument will automatically open up a browser window that will show your application. The application will take a few seconds to build, and then will display in your browser.

You’ll see the following output in your terminal:

Output** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **
...

Once the browser opens, you’ll see a default Angular app page.

If you don’t see these outputs, run through this step again and ensure that everything is correct. If you see an error such as: Port 4200 is already in use. Use '--port' to specify a different port then you can change the port number by typing:

ng serve --o --port <different-port-number>

The reason for this potential error message is because port 4200 on your machine is being used by another program or process. You can either, if you know what that process is, terminate it or you can follow the above step to specify a different port number.

You’ve now set up your application scaffolding. Next, you’ll create a weather component that will contain the main form and associated weather details of the search location.

Step 3 — Creating Your Weather Component

An Angular application is primarily made up of components, which are pieces of logic that have a particular function within an application. The component is composed of some logic that manages part of the screen in an application — this is called the view.

For example in this tutorial, you’re going to create a Weather Component that will be responsible for handling two tasks:

  • Searching for a location
  • Displaying associated weather data for that location

To achieve the first objective, you’ll create a form that will allow you to search for a location. When you click the search button on your form, it will trigger a function that will search for that location.

To achieve the second objective, you’ll have a <div> with nested <p> tags that will neatly display your retrieved data.

Whilst your app is running from your terminal window, you can’t type anything else in that particular window. Therefore, open up the <span class="highlight">weather-app</span> directory in a new terminal window if you want to execute other ng commands. Alternatively, you can stop the app from running in the original terminal window by pressing CTRL + C. You can then install the new component, and after that start the app again by typing ng serve --o.

Execute the following command that will create your Weather Component and automatically import it into your app.module.ts file. Remember that your app.module.ts file holds details about all the components, modules, and providers in your application.

ng generate component weather

You’ll see output like this (the exact byte sizes may vary):

OutputCREATE src/app/weather/weather.component.css (0 bytes)
CREATE src/app/weather/weather.component.html (26 bytes)
CREATE src/app/weather/weather.component.spec.ts (635bytes)
CREATE src/app/weather/weather.component.ts (273 bytes)
UPDATE src/app/app.module.ts (400 bytes)
...

This output shows that Angular has created the four files necessary for a component:

  • The .css and .html files for your view
  • .spec.ts file for testing your component
  • A.component.ts file to hold your component’s functions

Angular has also updated the src/app/app.module.ts file to add a reference to the newly created component. You’ll always find component files under the src/app/<span class="highlight">name-of-component</span> directory.

Now that you have installed your new component, return to your browser to see the app. If you stopped the app running to install the new component, start it again by typing:

ng serve --o

You’ll notice that you can still see “Welcome to app!” (the default component) displayed on the page. You can’t see your newly created component. In the next section, you’ll change this so that whenever you go to localhost:4200, you’ll access your newly created weather component instead of Angular’s default component.

Step 4 — Accessing Your Weather Component

In standard HTML, whenever you want to create a new page, you create a new .html file. For example, if you already had a pre-existing HTML page from which you wanted to navigate to your newly created page, you’d have an href attribute with an anchor tag to point to that new page. For example:

preexisting.html

<a href="/newpage.html">Go to New Page</a>

In Angular, however, this works slightly differently. You cannot use an href attribute in this way to navigate to a new component. When you want to link through to a component, you need to make use of Angular’s Router library and declare a desired URL path within a file that will map directly to a component.

In Angular, you call this file routes.ts. This holds all the details of your routes (links). For this file to work correctly, you will import the Routes type from the @angular/router library and list your desired links to be of type Routes. This will communicate to Angular that these are a list of routes for navigation in your app.

Create the file routes.ts in your text editor and save it in the src/app directory. Next, add the following content to the routes.ts file:

src/app/routes.ts

import { Routes } from '@angular/router'

Now, declare the URL path and the component in src/app/routes.ts. You want to make your app such that when you go to the homepage (http://localhost:4200), you access your newly created Weather Component. Add these lines to the file, which will map the root URL to the Weather Component you just created:

src/app/routes.ts

import { Routes } from '@angular/router'
import { WeatherComponent } from './weather/weather.component';

export const allAppRoutes: Routes = [
{ path: '', component: WeatherComponent }
];

You’ve imported your WeatherComponent, and then created a variable allAppRoutes that’s an array of type Routes. The allAppRoutes array holds route definition objects each containing a URL path and the component to map to. You’ve specified that any time you go to the root URL (’’), it should navigate to the WeatherComponent.

Your final routes.ts file will look like this:

src/app/routes.ts

import { Routes } from "@angular/router";
import { WeatherComponent } from "./weather/weather.component";

export const allAppRoutes: Routes = [
{ path: '', component: WeatherComponent }
];

You now need to add these routes to your main app.module.ts file. You need to pass the array you just created — allAppRoutes — into an Angular module called the RouterModule. The RouterModule will initialize and configure the Router (responsible for carrying out all app navigation) and provide it with its routing data from allAppRoutes. Add the following highlighted content:

src/app/app.module.ts

...
import {WeatherComponent} from './weather/weather.component';
import {RouterModule} from '@angular/router';
import {allAppRoutes} from './routes';
...
@NgModule({
declarations:[
...
],
imports: [
BrowserModule,
RouterModule.forRoot(allAppRoutes)
]
...
})
...

In this file, you’ve imported the RouterModule and allAppRoutes array of route objects. You’ve then passed the allAppRoutes array into the RouterModule so that your Router knows where to route your URLs to.

Lastly, you need to enable routing itself. Open the app.component.ts file. There’s a templateUrl property that specifies the HTML for that particular component: ./app.component.html. Open this file, src/app/app.component.html, and you will see that it contains all of the HTML for your localhost:4200 page.

Remove all of the HTML contained within app.component.html and replace it with:

src/app/app.component.html

<router-outlet></router-outlet>

The router-outlet tag activates routing and matches the URL the user types into the browser to the route definition you created earlier in the routes.ts file under the allAppRoutes variable. The router then displays the view in the HTML. In this tutorial, you’ll display the weather.component.html code directly after the <router-outlet></router-outlet> tag.

Now, if you navigate to http://localhost:4200, you will see weather works! appear on your page.

You’ve set up routing in your application. Next, you’ll create your form and details section that will enable you to search for a location and show its associated details.

Step 5 — Defining the User Interface

You’ll be using Bootstrap to act as the scaffolding for your application view. Bootstrap is useful for creating ready-made, responsive websites that adapt to any device (mobile, tablet, or desktop). It achieves this by treating every row on a webpage as twelve columns wide. On a webpage, a row is simply a line from one end of the page to the other. This means that every page’s content must be contained within that line, and it must equal twelve columns. If it doesn’t equal twelve columns, it’ll be pushed down to another row. For example, in Bootstrap’s grid system, there would be a twelve-column row divided into two sections of six columns, and the next twelve-column row divided into three sections of four columns.

In the Bootstrap documentation, you can read more about this grid system.

You’ll be splitting your page into two sections of six columns with the left column holding your search form and the right showing the weather details.

Open src/app/weather/weather.component.html to access your WeatherComponent HTML code. Delete the paragraph that is currently in the file, and then add the following code:

src/app/weather/weather.component.html

<div class="container">
<div class="row">
<div class="col-md-6"><h3 class="text-center">Search for Weather:</h3></div>
<div class="col-md-6"><h3 class="text-center">Weather Details:</h3></div>
</div>
</div>

You created a <div> with class container to hold all your content. You then created a row that you split into two sections of six columns each. The left-hand side will hold your search form and the right, your weather data.

Next, to build your form, you’ll work in the first col-md-6 column. You’ll also add a button that will submit what you’ve typed into your form to APIXU, which will then return the requested weather details. To do this, identify the first col-md-6 class and add the following highlighted content underneath the <h3> tag:

src/app/weather/weather.component.html

...
<div class="col-md-6">
<h3 class="text-center">Search for Weather:</h3>
<form>
<div class="form-group">
<input
class="form-control"
type="text"
id="weatherLocation"
aria-describedby="weatherLocation"
placeholder="Please input a Location"
/>
</div>
<div class="text-center">
<button type="submit" class="btn btn-success btn-md">
Search for the weather</button>
</div>
</form>
</div>
...

You’ve added your form and added a form-group class that holds your search bar. You’ve also created your button to search for the weather. In your browser, your weather app page will look like this:

This looks a little compact, so you can add some CSS in order to style the page with some better spacing. The major advantage of Bootstrap is that it comes with spacing classes that you can add to your HTML without needing to write any extra CSS of your own. If, however, there is any extra CSS you would like to incorporate that Bootstrap’s standard classes don’t cover, you can write in your own CSS as necessary. For this tutorial, you will use Bootstrap’s standard classes.

For every <h3> tag, you will add the .my-4 Boostrap CSS class. The m sets margin on the element, the y sets both margin-top and margin-bottom on the element, and finally 4 specifies the amount of margin to add. You can find out more details about the different spacing types and sizes here. In your weather.component.html file, add the following highlighted content to replace the current <h3> tags:

src/app/weather/weather.component.html

<div class="col-md-6">
<h3 class="text-center my-4">Search for Weather:</h3>
<form>
<div class="form-group">
<input
class="form-control"
type="text"
id="weatherLocation"
aria-describedby="weatherLocation"
placeholder="Please input a Location"
/>
</div>
<div class="text-center">
<button type="submit" class="btn btn-success btn-md">
Search for the weather
</button>
</div>
</form>
</div>
<div class="col-md-6">
<h3 class="text-center my-4">Weather Details:</h3>
</div>

Reload the page in your browser and you’ll see that you have more spacing.

You’ve created your form as well as the section where you’re going to display the information you receive from the APIXU API. Next, you’ll wire up your form to be able to input your location correctly.

Step 6 — Wiring Up Your Form

In Angular, there are two ways of creating forms for user input in your application — reactive or template-driven. Although they achieve the same result, each form type handles processing user input data differently.

With reactive forms, you create a list of the different elements of your form in your .component.ts file. You then connect them to your created HTML form within the respective .component.html file. This is strictly one-way; that is, data flows from your HTML to your .component.ts file, there is no bi-directional flow of data.

With template-driven forms, you create your form as you would in normal HTML. Then, using directives such as ngModel, you can create either one-way or two-way data bindings from your HTML, back to your data model in your component, and vice-versa.

There are strengths and weaknesses in each approach, but in general, reactive forms are preferable because of the:

  • Flexibility to create forms of varying complexities.
  • Simplicity to unit test by checking on the state of each form control in the component’s .component.ts file.
  • Capability to subscribe to values within a form. A developer can subscribe to the form’s value stream allowing them to perform some action on values being typed into the form in real time.

Despite these strengths, reactive forms can sometimes be more complex to implement. This can lead to developers writing more code than compared to a template-driven form. To see a comprehensive overview of both form types and best use cases, Angular’s official guide provides a good starting point. For this tutorial, you’ll be using reactive forms.

To use a reactive form, open the file app.module.ts. Next, import the ReactiveFormsModule by declaring the import toward the top of the file.

src/app/app.module.ts

...
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
...
})
...

Finally, add the ReactiveFormsModule to your list of imports.

src/app/app.module.ts

...
@NgModule({
...
imports: [
BrowserModule,
RouterModule.forRoot(allAppRoutes),
ReactiveFormsModule
]
...
})
...

Following these code additions, your app.module.ts will look like this:

src/app/app.module.ts

import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";

import { AppComponent } from "./app.component";
import { WeatherComponent } from "./weather/weather.component";
import { RouterModule } from "@angular/router";
import { allAppRoutes } from "./routes";
import { ReactiveFormsModule } from "@angular/forms";

@NgModule({
declarations: [AppComponent, WeatherComponent],
imports: [
BrowserModule,
RouterModule.forRoot(allAppRoutes),
ReactiveFormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}

Once you’ve added both of these lines, open the weather.component.ts file and import the FormBuilder and FormGroup classes.

src/app/weather/weather.component.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';

Now create a variable in your weather.component.ts file that will reference your FormGroup:

weather.component.ts

export class WeatherComponent implements OnInit {
public weatherSearchForm: FormGroup;
constructor() { }
...

Every time you want to perform an action on your form, you’ll reference it via the weatherSearchForm variable. You’ll now add the FormBuilder import into your constructor so that you can use it in your component.

weather.component.ts

...
public weatherSearchForm: FormGroup;
constructor(private formBuilder: FormBuilder) {}
...

By adding the formBuilder to the constructor, it creates an instance of the FormBuilder class, allowing you to use it within your component.

You are now ready to create your FormGroup and its respective values in the weather.component.ts file. If you have several input options in your form, it’s best practice to enclose it within a FormGroup. In this tutorial, you will only have one (your location input), but you will use the FormGroup anyway for practice.

It’s important that your form is ready for use when you navigate to your component. Because you’re using a reactive form, you must create the tree of elements within the form first before you bind it to the HTML. To achieve this, you need to ensure that you create your form elements in the ngOnInit hook inside your WeatherComponent. The ngOnInit method runs once at the initialization of a component, executing any logic that you specify needs to run before the component is ready to use.

You therefore have to create your form before you can complete the binding to HTML process.

In your WeatherComponent, you’ll initialize the form within the ngOnInit hook:

src/app/weather/weather.component.ts

...
constructor(private formBuilder: FormBuilder) {}
ngOnInit() {
this.weatherSearchForm = this.formBuilder.group({
location: ['']
});
}

You have created the first part of the form according to reactive form style: defining your form components in the weather.component.ts file. You’ve created a group of your form’s composite elements (at the moment, you have one element, location). The [''] array allows you to specify some extra options for your form inputs such as: pre-populating it with some data and using validators to validate your input. You have no need of any of these for this tutorial, so you can just leave it blank. You can find out more about what you can pass into an element property here.

You have two more things to do before your form is complete. First open up your weather.component.html file. You need to assign the form a property [formGroup]. This property will be equal to the variable you just declared in your weather.component.ts file: weatherSearchForm. Second, you have to bind your location element (declared in your weather.component.ts file) to your HTML. In weather.component.html, add the following highlighted content:

src/app/weather/weather.component.html

...
<form
[formGroup]="weatherSearchForm" >
<div class="form-group">
<input
class="form-control"
type="text"
id="weatherLocation"
aria-describedby="weatherLocation"
placeholder="Please input a Location"
/>formControlName="location" />
</div>
<div class="text-center">
<button type="submit" class="btn btn-success btn-md">
Search for the weather
</button>
</div>
</form>
...

You’ve added the [formGroup] property, binding your form to HTML. You’ve also added the formControlNameproperty that declares that this particular input element is bound to the location element in your weather.component.ts file.

Save your file and return to your browser, you’ll see that your app looks exactly the same. This means that your form is correctly wired up. If you see any errors at this stage, then please go back through the previous steps to ensure that everything is correct in your files.

Next, you’ll wire up your button to be able to accept input data into your form.

Step 7 — Connecting Your Button

In this step you’re going to connect your search button to your form in order to be able to accept the user’s input data. You’re also going to create the scaffolding for the method that will eventually send the user’s input data to the APIXU weather API.

If you take a look back at your code in weather.component.html, you can see that your button has a type submit:

src/app/weather/weather.component.html

<form>
...
<div class="text-center">
<button type="submit" class="btn btn-success btn-md">Search for the weather</button>
</div>
</form>

This is a standard HTML value that will submit your form values to some function to take action on.

In Angular, you specify that function in the (ngSubmit) event. When you click your button in your form, as long as it has a type of submit, it will trigger the (ngSubmit) event, which will subsequently call whatever method you have assigned to it. In this case, you want to be able to get the location that your user has typed in and send it to the APIXU API.

You’re going to first create a method to handle this. In your weather.component.ts, create a method sendToAPIXU() that will take one argument: the value(s) you’ve typed into your form. Add the following highlighted content to the file:

src/app/weather/weather.component.ts

...
ngOnInit() {
this.weatherSearchForm = this.formBuilder.group({
location: [""]
});
}

sendToAPIXU(formValues) {

}
...

Next, add the ngSubmit event to your HTML and pass the values of your submitted form into the sendToAPIXU()method:

weather.component.html

...
<form [formGroup]="weatherSearchForm" (ngSubmit)="sendToAPIXU(weatherSearchForm.value)">
...
</form>
...

You’ve added the ngSubmit event to your form, connected your method you want to run when you submit your form, and passed in the values of your weatherSearchForm as an argument to your handler method (weatherSearchForm.value). You can now test this works by using console.log to print out your formValues, in your sendToAPIXU() method, add the following highlighted content to weather.component.ts:

weather.component.ts

...
sendToAPIXU(formValues){
console.log(formValues);
}

Go to your browser and open your console by right clicking anywhere on your website page, and then click on Inspect Element. There will be a tab on the window that pops up called Console. Type London into your form. When you click on the Search for Weather button, you’ll see an object with your location enclosed.

Your output from the console is a JSON object {location: "London"}. If you wanted to access your location value, you can do this by accessing formValues.location. Similarly, if you had any other inputs inside your form, you would swap .location for any other element names you had.

Note:

All values of a reactive form are stored in an object — where the key is the name of the value you passed into the formBuilder.group({}).

The button is now wired up and can receive input correctly. Next, you’ll make the sendToAPIXU() method make an HTTP request to the APIXU API.

Step 8 — Calling the APIXU API

The APIXU API accepts location information, searches the current weather details for that location, and returns them back to the client. You’ll now modify your app so that it sends location data to the API, obtains the response, and then displays the results on your page.

In order to make HTTP requests in Angular, you have to import the HttpClientModule. Open your src/app/app.module.ts and add the following highlighted lines:

src/app/app.module.ts

...
import { ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
...
imports: [
BrowserModule,
RouterModule.forRoot(allAppRoutes),
ReactiveFormsModule,
HttpClientModule
]
...
})
...

Next, you need to write the code to make the HTTP call to the APIXU API. It’s best practice to create an Angular service to make HTTP requests. Separation of concerns is key in any app that you build. A service allows you to move all of those HTTP requests your app makes into one file that you can then call inside any .component.ts file you create. You could “legally” write in those HTTP requests in the specific .component.ts file, but this isn’t best practice. You may, for instance, find that some of your requests are complex and require you to perform some post-processing actions after receiving your data. Several different components in your app might use some of your HTTP requests, and you don’t want to write the same method multiple times.

From a new terminal window or by stopping the server in your current terminal session, execute the following command to create a service called apixu:

ng g service apixu

You’ll see output resembling the following:

Outputcreate src/app/apixu.service.spec.ts (328 bytes)
create src/app/apixu.service.ts (134 bytes)
...

The command created the service file (apixu.service.ts) and a test file (apixu.service.spec.ts).

You now need to add this service as a provider into your app.module.ts file. This makes it available to use inside your app. Open this file, and first import the ApixuService:

src/app/app.module.ts

...
import { HttpClientModule } "@angular/common/http";
import { ApixuService } from "./apixu.service";
...

Next add the newly imported ApixuService as a provider into the providers block:

src/app/app.module.ts file

...
@NgModule({
...
providers: [ApixuService],
...
})
...

In Angular, if you want to use a service that you have created, you need to specify that service as a provider within your module.ts file. In this case, you’ve specified it as a provider within your entire application in app.module.ts.

Finally, open up the src/app/apixu.service.ts file. You’ll see the boilerplate code of what you need to create a service: first the import of the Injectable interface from Angular; then the fact that the service should be with the providedIn root injector (for the entire application); and then the decorating (this effectively means specifying) of your service as @Injectable.

src/app/apixu.service.ts

import { Injectable } from '@angular/core';

@Injectable({
providedIn: 'root'
})
export class ApixuService {

constructor() { }
}

The decorating of the service as @Injectable allows you to inject this service within the constructor in weather.component.ts so that you can use it inside your component.

If you stopped your application, restart it by running:

ng serve --o

As aforementioned, your service needs to make HTTP requests to the APIXU API and import the HttpClientModulein the app.module.ts file to make HTTP requests throughout the application. You additionally need to import the HttpClient library into the apixu.service.ts file to make HTTP requests to the APIXU API from the apixu.service.ts file itself. Open the apixu.service.ts file, and add the following highlighted content:

src/app/apixu.service.ts

...
import { HttpClient } from '@angular/common/http';
...

Now you need to write a method, getWeather(), that takes in one paramater: location. This method will make an API request to APIXU and return the retrieved location data.

For this, you’ll need the provided API key when you signed up for the APIXU API. If you log in to APIXU, you’ll come to the dashboard:

You will see your key, and below that, links to the API URL with your key already pre-filled for both the Current Weather and Forecast Weather. Copy the HTTPS link for the Current Weather details, it will be something like:

https://api.apixu.com/v1/current.json?key=<span class="highlight">YOUR_API_KEY</span>&q=Paris

This URL will give you current weather details for Paris. You want to be able to to pass in the location from your form into the &q= parameter instead. Therefore, remove Paris from the URL as you add it to your apixu.service.ts file:

src/app/apixu.service.ts

...
export class ApixuService {

constructor(private http: HttpClient) {}

getWeather(location){
return this.http.get(
'https://api.apixu.com/v1/current.json?key=YOUR_API_KEY&q=' + location
);
}
}

Note: You’ve used the API key directly within the code. In a production situation, you should store this securely server-side, and retrieve this key in a secure manner and use it within your application. You can either store it securely server-side, or use a key management application such as Hashicorp Vault or Azure Key Vault, to name a few.

You’ve now imported and injected HttpClient into the constructor so that you can use it. You’ve also created a method getWeather() that takes a location parameter and makes a GET request to your provided URL. You left the &q= parameter blank, as you’re going to provide this location directly from location parameter in the method. Lastly, you’ve returned the data back to whoever called the method.

Your service is now complete. You need to import your service into your WeatherComponent, inject it into your constructor to use it, and then update your sendToAPIXU() method to send your location to your newly created service. Open the weather.component.ts file to complete these tasks by adding the highlighted content:

src/app/weather.component.ts

...
import { FormBuilder, FormGroup } from "@angular/forms";
import { ApixuService } from "../apixu.service";
...
constructor(
private formBuilder: FormBuilder,
private apixuService: ApixuService
) {}
...
ngOnInit(){...}
sendToAPIXU(formValues){
this.apixuService
.getWeather(formValues.location)
.subscribe(data => console.log(data));
}

You’ve removed the former console.log statement in your sendToAPIXU() method and updated it with this content. You’re now passing in your location from your form to the sendToAPIXU() method you created earlier. You’ve then passed that data to the getWeather() method of the ApixuService that has subsequently made an HTTP request to the API with that location. You’ve then subscribed to the response you got back and, in this example, logged that data to the console. You always have to call the subscribe method on an HTTP request as the request will not begin until you have a way of reading the Observable response you get back. Observables are a way of sending messages between publishers and subscribers, allowing you to pass any kind of data back and forth. You will not be able to receive data from an observable until a subscriber has subscribed to it, because it won’t execute before that point.

Open the console in your browser again again. Now, type in London, UK and click Search for Weather. If you click on the tab arrows, you’ll see a list of the weather details in the console.

The output shows JSON objects containing all of the weather information needed. You have two objects returned: a current object and a location object. The former gives the desired weather details and the latter details about your location.

You’ve now got your weather data successfully showing in the console. To finish this tutorial, you’ll display these weather details in your HTML.

Step 9 — Displaying Your Weather Data in Your App

Displaying the results in the console is a good initial step to check that everything is working. However, you want to eventually show the weather data in HTML for your users. To do this, you’ll create a variable to hold your returned weather data, and then display that using interpolation in your HTML.

Interpolation allows you to display data in your views. To do this, it requires you to bind a property via the {{ }}style, to show that property in your HTML.

Open up the weather.component.ts file and create a variable called weatherData to which you’ll assign the retrieved JSON data from the API. Additionally, remove the code that was previously in the .subscribe() brackets and replace it with the following highlighted code:

src/app/weather/weather.component.ts

...
export class WeatherComponent implements OnInit {
public weatherSearchForm: FormGroup;
public weatherData: any;
...
sendToAPIXU(formValues){
this.apixuService
.getWeather(formValues.location)
.subscribe(data => this.weatherData = data)
console.log(this.weatherData);
}
}

You’ve created the variable weatherData and declared that it can hold data of any type. You’ve then assigned the data you receive back from your API call to that variable. Finally, you’ve added a console.log() statement to double check that weatherData holds all of your retrieved information.

Your weather.component.ts file should be looking like this at this stage:

src/app/weather/weather.component.ts

import { Component, OnInit } from "@angular/core";
import { FormBuilder, FormGroup } from "@angular/forms";
import { ApixuService } from "../apixu.service";

@Component({
selector: "app-weather",
templateUrl: "./weather.component.html",
styleUrls: ["./weather.component.css"]
})
export class WeatherComponent implements OnInit {
public weatherSearchForm: FormGroup;
public weatherData: any;

constructor(
private formBuilder: FormBuilder,
private apixuService: ApixuService
) {}

ngOnInit() {
this.weatherSearchForm = this.formBuilder.group({
location: [""]
});
}

sendToAPIXU(formValues) {
this.apixuService.getWeather(formValues.location).subscribe(data => {
this.weatherData = data;
console.log(this.weatherData);
});
}
}

If you go back and search for London, UK again, you’ll see your object printed out to the console as normal. Now, you want to show this data in your HTML. If you examine the current object from the retrieved weather data in the console, you’ll see values such as conditionfeelslike_cfeelslike_ftemp_ctemp_f, and so on You’re going to make use of all five of these properties.

Open your weather.component.html file again and add in the subtitles to the data you want to display. You’ll be adding these <p> tags within the second col-md-6:

src/app/weather/weather.component.html

...
<div class="col-md-6">
<h3 class="text-center my-4">Weather Details:</h3>
<p class="text-center">Current weather conditions:</p>
<p class="text-center">Temperature in Degrees Celsius:</p>
<p class="text-center">Temperature in Degrees Farenheit:</p>
<p class="text-center">Feels like in Degrees Celsius:</p>
<p class="text-center">Feels like in Degrees Farenheit:</p>
<p class="text-center">Location Searched:</p>
</div>

Next, you’ll add the data you have received from your JSON object to your HTML:

weather.component.html

...
<h3 class="text-center my-4 ">Weather Details:</h3>
<p class="text-center">
Current weather conditions: {{this.weatherData?.current.condition.text}}
</p>
<p class="text-center">
Temperature in Degrees Celsius: {{this.weatherData?.current.temp_c}}
</p>
<p class="text-center">
Temperature in Degrees Farenheit: {{this.weatherData?.current.temp_f}}
</p>
<p class="text-center">
Feels like in Degrees Celsius: {{this.weatherData?.current.feelslike_c}}
</p>
<p class="text-center">
Feels like in Degrees Farenheit:
{{this.weatherData?.current.feelslike_f}}
</p>
<p class="text-center">
Location Searched: {{this.weatherData?.location.name}},
{{this.weatherData?.location.country}}
</p>

You have used an operator ? as you retrieved data from your weatherData variable within your HTML. This operator is called an Elvis Operator.

Because you’re making an HTTP call, you’re making an asynchronous request. You’ll get that data back at some point, but it will not be an immediate response. Angular, however, will still continue to fill out your HTML with the data you specified from the weatherData variable. If you haven’t received data back by the time that Angular begins to populate your paragraphs, there will be an error stating that Angular can’t find that data. For example, .current or .location would be showing as undefined.

The Elvis Operator is a safe navigator and prevents this from happening. It tells Angular to wait and check if weatherData is first defined, before going ahead and showing that data in the HTML. Once weatherData has all of its information, Angular will then update your bindings and show your data as normal.

You final weather.component.ts file will look like the following:

weather.component.html

<div class="container">
<div class="row">
<div class="col-md-6">
<h3 class="text-center my-4">Search for Weather:</h3>
<form
[formGroup]="weatherSearchForm"
(ngSubmit)="sendToAPIXU(weatherSearchForm.value)"
>
<div class="form-group">
<input
class="form-control"
type="text"
id="weatherLocation"
aria-describedby="weatherLocation"
placeholder="Please input a Location"
formControlName="location"
/>
</div>
<div class="text-center">
<button type="submit" class="btn btn-success btn-md">
Search for the weather
</button>
</div>
</form>
</div>
<div class="col-md-6">
<h3 class="text-center my-4">Weather Details:</h3>
<p class="text-center">
Current weather conditions: {{ this.weatherData?.current.condition.text
}}.
</p>
<p class="text-center">
Temperature in Degrees Celsius: {{ this.weatherData?.current.temp_c }}
</p>
<p class="text-center">
Temperature in Degrees Farenheit: {{ this.weatherData?.current.temp_f }}
</p>
<p class="text-center">
Feels like in Degrees Celsius: {{ this.weatherData?.current.feelslike_c
}}
</p>
<p class="text-center">
Feels like in Degrees Farenheit: {{
this.weatherData?.current.feelslike_f }}
</p>
<p class="text-center">
Location Searched: {{ this.weatherData?.location.name }}, {{
this.weatherData?.location.country }}.
</p>
</div>
</div>
</div>

You’ve followed the pattern of the returned JSON weather object in order to output your desired data. Save your file, go back to your browser, and type London, UK, you’ll see your weather data appear on the right-hand side.

Try it with different locations, like: San Francisco, USDakar, Senegal, and Honololu, Hawaii. You’ll see the respective weather data appear for all those locations.

Conclusion

You have created a weather app using Angular, Bootstrap, and the APIXU API. You have set up an Angular project from scratch, following Angular best practices while ensuring your application is well designed and set up appropriately.

Angular is an advanced framework allowing you to create anything from small web applications to large, complex ones with ease. Angular, as with any frameworks, does have a learning curve, but small projects like this one can help you to quickly learn and start using it productively.

Another feature to consider adding to your application is handling errors from your HTTP requests; for instance, if you were to type in an invalid location. Another enhancement would be displaying different images if the temperature is between certain thresholds. You can also create different applications with Angular using other APIs.

You may also want to use NgBootstrap, which is a special type of Bootstrap built for Angular. This allows you to use all the standard Bootstrap JavaScript widgets as well as some special ones not included in the standard installation specifically adapted for Angular.

The full code for this tutorial is available on GitHub.

Originally published by nnamdiekweekwe at https://www.digitalocean.com

Learn more

Angular 7 (formerly Angular 2) - The Complete Guide

☞ http://academy.learnstartup.net/p/H1jE_tD3l

NativeScript + Angular: Build Native iOS, Android & Web Apps

☞ http://academy.learnstartup.net/p/bjuKTO9qo

Ionic 4 Crash Course with Heartstone API & Angular

☞ http://academy.learnstartup.net/p/Ts9XxIqyo

Angular & NodeJS - The MEAN Stack Guide

☞ http://academy.learnstartup.net/p/Skf7ILFw3l

Angular 7 CRUD with Nodejs and MySQL Example

Angular 7 CRUD with Nodejs and MySQL Example

Angular7 CRUD with nodejs and mysql example - Hey there, Today we will proceed to create a demo for CRUD with Mysql, Express, Angular7(MEAN) and Nodejs from scratch using Angular CLI

Below are the requirements for creating the CRUD on MEAN

  • Node.js
  • Angular CLI
  • Angular 7
  • Mysql
  • IDE or Text Editor

We assume that you have already available the above tools/frameworks and you are familiar with all the above that what individually actually does.

So now we will proceed step by step to achieve the task.

1. Update Angular CLI and Create Angular 7 Application

At first, We have to update the Angular CLI to the latest version. Open the terminal then go to the project folder and then type the below command to update the Angular CLI

sudo npm install -g @angular/cli

Once the above task finishes, Next task is to create new angular application with below command. So go to your project folder and then type below command:

ng new angular7-crud

then go to the newly created folder of angular application with cd /angular7-crud  and type **ng serve. **Now, open the browser then go to http://localhost:4200 you should see this page.

Angular 7 CRUD with Nodejs and MySQL Example

2. Create a server with node.js express and Mysql for REST APIs

create a separate folder named server for server-side stuff, Then move inside folder and create server.js by typing touch server.js

Let’s have a look on the server.js file

let app = require('express')(),
server = require('http').Server(app),
bodyParser = require('body-parser')
express = require('express'),
cors = require('cors'),
http = require('http'),
path = require('path');
 
let articleRoute = require('./Routes/article'),
util = require('./Utilities/util');
 
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false }));
 
app.use(cors());
 
app.use(function(err, req, res, next) {
return res.send({ "statusCode": util.statusCode.ONE, "statusMessage": util.statusMessage.SOMETHING_WENT_WRONG });
});
 
app.use('/article', articleRoute);
 
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next();
});
 
/*first API to check if server is running*/
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '../server/client/dist/index.html'));
})
 
 
server.listen(3000,function(){
console.log('app listening on port: 3000');
});

In the above file we can see, at the top, there are required packages for the app. Below that body parsing, middleware and routing is done.

The next task is to create routes and create a file article.js . So creating a folder name ‘Routes’ and adding article.js within it.

Add the below code for routing in article.js inside routing folder

let express = require('express'),
router = express.Router(),
util = require('../Utilities/util'),
articleService = require('../Services/article');
 
/**Api to create article */
router.post('/create-article', (req, res) => {
articleService.createArticle(req.body, (data) => {
res.send(data);
});
});
 
// /**Api to update article */
router.put('/update-article', (req, res) => {
articleService.updateArticle(req.body, (data) => {
res.send(data);
});
});
 
// /**Api to delete the article */
router.delete('/delete-article', (req, res) => {
articleService.deleteArticle(req.query, (data) => {
res.send(data);
});
});
 
/**Api to get the list of article */
router.get('/get-article', (req, res) => {
documentService.getArticle(req.query, (data) => {
res.send(data);
});
});
 
// /**API to get the article by id... */
router.get('/get-article-by-id', (req, res) => {
articleService.getArticleById(req.query, (data) => {
res.send(data);
});
});
 
module.exports = router;

Now create a folder named Utilities for all config, common methods and mysql connection config.

Now I am adding config values in a file named config.js

let environment = "dev";
 
let serverURLs = {
"dev": {
"NODE_SERVER": "http://localhost",
"NODE_SERVER_PORT": "3000",
"MYSQL_HOST": 'localhost',
"MYSQL_USER": 'root',
"MYSQL_PASSWORD": 'password',
'MYSQL_DATABASE': 'demo_angular7_crud',
}
}
 
let config = {
"DB_URL_MYSQL": {
"host": `${serverURLs[environment].MYSQL_HOST}`,
"user": `${serverURLs[environment].MYSQL_USER}`,
"password": `${serverURLs[environment].MYSQL_PASSWORD}`,
"database": `${serverURLs[environment].MYSQL_DATABASE}`
},
"NODE_SERVER_PORT": {
"port": `${serverURLs[environment].NODE_SERVER_PORT}`
},
"NODE_SERVER_URL": {
"url": `${serverURLs[environment].NODE_SERVER}`
}
};
 
module.exports = {
config: config
};

Now configure mysql connection. So I am writing the connection with database in a separate file. So creating a file named mysqkConfig.js under Utilities folder and adding the below line of code for mysql connection:

var config = require("../Utilities/config").config;
var mysql = require('mysql');
var connection = mysql.createConnection({
host: config.DB_URL_MYSQL.host,
user: config.DB_URL_MYSQL.user,
password: config.DB_URL_MYSQL.password,
database: config.DB_URL_MYSQL.database,
});
 
connection.connect(() => {
require('../Models/Article').initialize();
});
 
let getDB = () => {
return connection;
}
 
module.exports = {
getDB: getDB
}

Now I am creating separate file name util.js to save common methods and common status code/message:

// Define Error Codes
let statusCode = {
OK: 200,
FOUR_ZERO_FOUR: 404,
FOUR_ZERO_THREE: 403,
FOUR_ZERO_ONE: 401,
FIVE_ZERO_ZERO: 500
};
 
// Define Error Messages
let statusMessage = {
SERVER_BUSY : 'Our Servers are busy. Please try again later.',
DATA_UPDATED: 'Data updated successfully.',
DELETE_DATA : 'Delete data successfully',
 
};
 
module.exports = {
statusCode: statusCode,
statusMessage: statusMessage
}

Now the next part is model, So create a folder named Models and create a file **Article.js **and add the below code in it:

let mysqlConfig = require("../Utilities/mysqlConfig");
 
let initialize = () => {
mysqlConfig.getDB().query("create table IF NOT EXISTS article (id INT auto_increment primary key, category VARCHAR(30), title VARCHAR(24))");
 
}
 
module.exports = {
initialize: initialize
}

Now create DAO folder and add a file articleDAO.js for writting the mysql queries common functions:

let dbConfig = require("../Utilities/mysqlConfig");


 
let getArticle = (criteria, callback) => {
//criteria.aricle_id ? conditions += ` and aricle_id = '${criteria.aricle_id}'` : true;
dbConfig.getDB().query(`select * from article where 1`,criteria, callback);
}
 
let getArticleDetail = (criteria, callback) => {
    let conditions = "";
criteria.id ? conditions += ` and id = '${criteria.id}'` : true;
dbConfig.getDB().query(`select * from article where 1 ${conditions}`, callback);
}
 
let createArticle = (dataToSet, callback) => {
console.log("insert into article set ? ", dataToSet,'pankaj')
dbConfig.getDB().query("insert into article set ? ", dataToSet, callback);
}
 
let deleteArticle = (criteria, callback) => {
let conditions = "";
criteria.id ? conditions += ` and id = '${criteria.id}'` : true;
console.log(`delete from article where 1 ${conditions}`);
dbConfig.getDB().query(`delete from article where 1 ${conditions}`, callback);
 
}
 
let updateArticle = (criteria,dataToSet,callback) => {
    let conditions = "";
let setData = "";
criteria.id ? conditions += ` and id = '${criteria.id}'` : true;
dataToSet.category ? setData += `category = '${dataToSet.category}'` : true;
dataToSet.title ? setData += `, title = '${dataToSet.title}'` : true;
console.log(`UPDATE article SET ${setData} where 1 ${conditions}`);
dbConfig.getDB().query(`UPDATE article SET ${setData} where 1 ${conditions}`, callback);
}
module.exports = {
getArticle : getArticle,
createArticle : createArticle,
deleteArticle : deleteArticle,
updateArticle : updateArticle,
getArticleDetail : getArticleDetail
}

Now one create Services folder and add a file article.js for all the logic of API

let async = require('async'),
parseString = require('xml2js').parseString;
 
let util = require('../Utilities/util'),
articleDAO = require('../DAO/articleDAO');
//config = require("../Utilities/config").config;
 
 
/**API to create the atricle */
let createArticle = (data, callback) => {
async.auto({
article: (cb) => {
var dataToSet = {
"category":data.category?data.category:'',
"title":data.title,
}
console.log(dataToSet);
articleDAO.createArticle(dataToSet, (err, dbData) => {
if (err) {
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage": util.statusMessage.SERVER_BUSY });
return;
}
 
cb(null, { "statusCode": util.statusCode.OK, "statusMessage": util.statusMessage.DATA_UPDATED,"result":dataToSet });
});
}
//]
}, (err, response) => {
callback(response.article);
});
}
 
/**API to update the article */
let updateArticle = (data,callback) => {
async.auto({
articleUpdate :(cb) =>{
if (!data.id) {
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage": util.statusMessage.PARAMS_MISSING })
return;
}
console.log('phase 1');
var criteria = {
id : data.id,
}
var dataToSet={
"category": data.category,
"title":data.title,
}
console.log(criteria,'test',dataToSet);
                    articleDAO.updateArticle(criteria, dataToSet, (err, dbData)=>{
                        if(err){
cb(null,{"statusCode":util.statusCode.FOUR_ZERO_ONE,"statusMessage":util.statusMessage.SERVER_BUSY});
                        return; 
                        }
                        else{
cb(null, { "statusCode": util.statusCode.OK, "statusMessage": util.statusMessage.DATA_UPDATED,"result":dataToSet });                        
                        }
                    });
}
}, (err,response) => {
callback(response.articleUpdate);
});
}
 
/**API to delete the subject */
let deleteArticle = (data,callback) => {
console.log(data,'data to set')
async.auto({
removeArticle :(cb) =>{
if (!data.id) {
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage": util.statusMessage.PARAMS_MISSING })
return;
}
var criteria = {
id : data.id,
}
articleDAO.deleteArticle(criteria,(err,dbData) => {
if (err) {
console.log(err);
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage": util.statusMessage.SERVER_BUSY });
return;
}
cb(null, { "statusCode": util.statusCode.OK, "statusMessage": util.statusMessage.DELETE_DATA });
});
}
}, (err,response) => {
callback(response.removeArticle);
});
}
 
/***API to get the article list */
let getArticle = (data, callback) => {
async.auto({
article: (cb) => {
articleDAO.getArticle({},(err, data) => {
if (err) {
cb(null, {"errorCode": util.statusCode.INTERNAL_SERVER_ERROR,"statusMessage": util.statusMessage.SERVER_BUSY});
return;
}
cb(null, data);
return;
});
}
}, (err, response) => {
callback(response.article);
})
}
 
/***API to get the article detail by id */
let getArticleById = (data, callback) => {
async.auto({
article: (cb) => {
let criteria = {
"id":data.id
}
articleDAO.getArticleDetail(criteria,(err, data) => {
if (err) {
console.log(err,'error----');
cb(null, {"errorCode": util.statusCode.INTERNAL_SERVER_ERROR,"statusMessage": util.statusMessage.SERVER_BUSY});
return;
}
cb(null, data[0]);
return;
});
}
}, (err, response) => {
callback(response.article);
})
}
 
module.exports = {
createArticle : createArticle,
updateArticle : updateArticle,
deleteArticle : deleteArticle,
getArticle : getArticle,
getArticleById : getArticleById
};

3. Create angular component for performing CRUD task of article

ng g component article

Above command will generate all required files for build article component and also automatically added this component to app.module.ts.

create src/app/article/article.component.css (0 bytes)
create src/app/article/article.component.html (23 bytes)
create src/app/article/article.component.spec.ts (614 bytes)
create src/app/article/article.component.ts (321 bytes)
update src/app/app.module.ts (390 bytes)

Now we need to add HttpClientModule to app.module.ts. Open and edit src/app/app.module.ts then add this import. And add it to @NgModule imports after BrowserModule. Now our app.module.ts will have following code:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
 
import { AppComponent } from './app.component';
import { ArticleComponent } from './article.component';
import { ArticleService } from './article.service';
 
@NgModule({
imports: [
BrowserModule,
HttpModule,
ReactiveFormsModule
],
declarations: [
AppComponent,
ArticleComponent
],
providers: [
ArticleService
],
bootstrap: [
AppComponent
]
})
export class AppModule { }

Now create a service file where we will make all the request to the server for CRUD operation. Command for creating service is ng g service artcle , for now I have just created a file named it article.service.ts. Let's have a look in the code inside this file.

import { Injectable } from '@angular/core';
import { Http, Response, Headers, URLSearchParams, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
 
import { Article } from './article';
 
@Injectable()
export class ArticleService {
//URL for CRUD operations
    articleUrl = "http://localhost:3000/article";
    //Create constructor to get Http instance
    constructor(private http:Http) {
    }
    
    //Fetch all articles
getAllArticles(): Observable<Article[]> {
return this.http.get(this.articleUrl+"/get-article")
              .map(this.extractData)
         .catch(this.handleError);
 
}
    //Create article
createArticle(article: Article):Observable<number> {
     let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: cpHeaders });
return this.http.post(this.articleUrl+"/create-article", article, options)
.map(success => success.status)
.catch(this.handleError);
}
    //Fetch article by id
getArticleById(articleId: string): Observable<Article> {
        let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: cpHeaders });
        console.log(this.articleUrl +"/get-article-by-id?id="+ articleId);
        return this.http.get(this.articleUrl +"/get-article-by-id?id="+ articleId)
             .map(this.extractData)
             .catch(this.handleError);
}   
    //Update article
updateArticle(article: Article):Observable<number> {
     let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: cpHeaders });
return this.http.put(this.articleUrl +"/update-article", article, options)
.map(success => success.status)
.catch(this.handleError);
}
//Delete article    
deleteArticleById(articleId: string): Observable<number> {
        let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: cpHeaders });
        return this.http.delete(this.articleUrl +"/delete-article?id="+ articleId)
             .map(success => success.status)
             .catch(this.handleError);
}   
    private extractData(res: Response) {
        let body = res.json();
return body;
}
private handleError (error: Response | any) {
        console.error(error.message || error);
        return Observable.throw(error.status);
}
}

In the above file we have made all the http request for the CRUD operation. Observables of rxjs library has been used to handle the data fetching from http request.

Now let's move to the next file, article.component.ts. Here we have all the login part of the app. Let's have a look code inside this file:

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
 
import { ArticleService } from './article.service';
import { Article } from './article';
 
@Component({
selector: 'app-article',
templateUrl: './article.component.html',
styleUrls: ['./article.component.css']
})
export class ArticleComponent implements OnInit {
//Component properties
allArticles: Article[];
statusCode: number;
requestProcessing = false;
articleIdToUpdate = null;
processValidation = false;
//Create form
articleForm = new FormGroup({
title: new FormControl('', Validators.required),
category: new FormControl('', Validators.required)   
});
//Create constructor to get service instance
constructor(private articleService: ArticleService) {
}
//Create ngOnInit() and and load articles
ngOnInit(): void {
     this.getAllArticles();
}
//Fetch all articles
 
getAllArticles() {
        this.articleService.getAllArticles()
         .subscribe(
data => this.allArticles = data,
                errorCode => this.statusCode = errorCode);
                
}
//Handle create and update article
onArticleFormSubmit() {
     this.processValidation = true;
     if (this.articleForm.invalid) {
     return; //Validation failed, exit from method.
     }
     //Form is valid, now perform create or update
this.preProcessConfigurations();
     let article = this.articleForm.value;
     if (this.articleIdToUpdate === null) {
     //Generate article id then create article
this.articleService.getAllArticles()
     .subscribe(articles => {
            
         //Generate article id    
         let maxIndex = articles.length - 1;
         let articleWithMaxIndex = articles[maxIndex];
         let articleId = articleWithMaxIndex.id + 1;
         article.id = articleId;
         console.log(article,'this is form data---');
         //Create article
    this.articleService.createArticle(article)
             .subscribe(successCode => {
                    this.statusCode = successCode;
                    this.getAllArticles();  
                    this.backToCreateArticle();
                 },
                 errorCode => this.statusCode = errorCode
             );
         });        
     } else {
  //Handle update article
article.id = this.articleIdToUpdate;        
     this.articleService.updateArticle(article)
     .subscribe(successCode => {
         this.statusCode = successCode;
                 this.getAllArticles();  
                    this.backToCreateArticle();
             },
         errorCode => this.statusCode = errorCode);  
     }
}
//Load article by id to edit
loadArticleToEdit(articleId: string) {
this.preProcessConfigurations();
this.articleService.getArticleById(articleId)
     .subscribe(article => {
            console.log(article,'poiuytre');
         this.articleIdToUpdate = article.id;
                    this.articleForm.setValue({ title: article.title, category: article.category });
                    this.processValidation = true;
                    this.requestProcessing = false;
         },
         errorCode => this.statusCode = errorCode);
}
//Delete article
deleteArticle(articleId: string) {
this.preProcessConfigurations();
this.articleService.deleteArticleById(articleId)
     .subscribe(successCode => {
         //this.statusCode = successCode;
                    //Expecting success code 204 from server
                    this.statusCode = 204;
                 this.getAllArticles();  
                 this.backToCreateArticle();
             },
         errorCode => this.statusCode = errorCode);
}
//Perform preliminary processing configurations
preProcessConfigurations() {
this.statusCode = null;
     this.requestProcessing = true;
}
//Go back from update to create
backToCreateArticle() {
this.articleIdToUpdate = null;
this.articleForm.reset(); 
     this.processValidation = false;
}
}

Now we have to show the task over browser, So lets have a look inside article.component.html file.

<h1 class="text-center">Angular 7 CRUD Demo App</h1>
<h3 class="text-center" *ngIf="articleIdToUpdate; else create">
Update Article for Id: {{articleIdToUpdate}}
</h3>
<ng-template #create>
<h3 class="text-center"> Create New Article </h3>
</ng-template>
<div>
<form [formGroup]="articleForm" (ngSubmit)="onArticleFormSubmit()">
<table class="table-striped" style="margin:0 auto;">
<tr><td>Enter Title</td><td><input formControlName="title">
   <label *ngIf="articleForm.get('title').invalid && processValidation" [ngClass] = "'error'"> Title is required. </label>
 </td></tr>
<tr><td>Enter Category</td><td><input formControlName="category">
   <label *ngIf="articleForm.get('category').invalid && processValidation" [ngClass] = "'error'"> Category is required. </label>
  </td></tr>  
<tr><td colspan="2">
   <button class="btn btn-default" *ngIf="!articleIdToUpdate">CREATE</button>
    <button class="btn btn-default" *ngIf="articleIdToUpdate">UPDATE</button>
   <button (click)="backToCreateArticle()" *ngIf="articleIdToUpdate">Go Back</button>
  </td></tr>
</table>
</form>
<br/>
<div class="text-center" *ngIf="statusCode; else processing">
<div *ngIf="statusCode === 201" [ngClass] = "'success'">
   Article added successfully.
</div>
<div *ngIf="statusCode === 409" [ngClass] = "'success'">
Article already exists.
</div>   
<div *ngIf="statusCode === 200" [ngClass] = "'success'">
Article updated successfully.
</div>   
<div *ngIf="statusCode === 204" [ngClass] = "'success'">
Article deleted successfully.
</div>   
<div *ngIf="statusCode === 500" [ngClass] = "'error'">
Internal Server Error.
</div> 
</div>
<ng-template #processing>
  <img *ngIf="requestProcessing" src="assets/images/loading.gif">
</ng-template>
</div>
<h3 class="text-center">Article List</h3>
<table class="table-striped" style="margin:0 auto;" *ngIf="allArticles">
<tr><th> Id</th> <th>Title</th><th>Category</th><th></th><th></th></tr>
<tr *ngFor="let article of allArticles" >
<td>{{article.id}}</td> <td>{{article.title}}</td> <td>{{article.category}}</td>
  <td><button class="btn btn-default" type="button" (click)="loadArticleToEdit(article.id)">Edit</button> </td>
  <td><button class="btn btn-default" type="button" (click)="deleteArticle(article.id)">Delete</button></td>
</tr>
</table>

Now since I have created server and client two separate folder for nodejs and angular task. So will run both the apps with npm start over two tabs of terminal.

On the browser, over link http://localhost:4200. App will look like below

Angular CRUD with Nodejs and MySQL Example

That’s all for now. Thank you for reading and I hope this post will be very helpful for creating CRUD operations with angular7,node.js & mysql.

================================================

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter

Angular 8 Tutorial By Example: (REST API, HttpClient GET, Components, Services & ngFor)

Angular 8 Tutorial By Example: (REST API, HttpClient GET, Components, Services & ngFor)

Angular 8 Tutorial By Example: (REST API, HttpClient GET, Components, Services & ngFor) - In this tutorial, you'll learn by example how to send GET requests to REST API servers in your Angular 8 application using HttpClient...

Angular 8 Tutorial By Example: (REST API, HttpClient GET, Components, Services & ngFor) - In this tutorial, you'll learn by example how to send GET requests to REST API servers in your Angular 8 application using HttpClient...

We’ll also learn how to use the basic concepts of Angular like components and services and how to use the ngFor directive to display collections of data.

We’ll be consuming a JSON API available from NewsAPI.org

Throughout this tutorial, we are going to build a simple example from scratch using Angular CLI 8 and we’ll see how to use HttpClient to send GET requests to third-party REST API servers and how to consume and display the returned JSON data.

In more details, we'll learn:

  • How to create an Angular 8 project using Angular CLI,
  • How to quickly set up routing in our project,
  • How to create Angular components and services,
  • How to subscribe to Observables,
  • How to use the ngFor directive in templates to iterate over data.

You can also follow this tutorial as a video:

Prerequisites

Before getting started, you need a few requirements. You need to have the following tools installed on your development machine:

  • Node.js and npm. You can install both of them from the official website.
  • Angular CLI 8 (You can install it from npm using: npm install -g @angular/cli)
Creating an Angular 8 Project

Now let’s create our Angular 8 project. Open a new terminal and run the following command:

$ ng new angular-httpclient-demo

The CLI will prompt you if Would you like to add Angular routing? (y/N), type y. And Which stylesheet format would you like to use? Choose CSS and type Enter.

Next, you can serve your application locally using the following commands:

$ cd ./angular-httpclient-demo
$ ng serve

Your application will be running from <a href="http://localhost:4200" target="_blank">http://localhost:4200</a>.

Getting News Data

Before you can fetch the news data from NewsAPI.org which offers a free plan for open source and development projects, you first need to go the register page for getting an API key.

Adding an Angular Service

Next, let’s create a service that will take care of getting data from the news API. Open a new terminal and run the following command:

$ ng generate service api

Setting up HttpClient

Next, open the src/app/app.module.ts file then import HttpClientModule and add it to the importsarray:

// [...]
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [
    // [...]
    HttpClientModule,
  ],
  // [...]
})
export class AppModule {}

That's all, we are now ready to use the HttpClient in our project.

Injecting HttpClient in The Angular Service

Next, open the src/app/api.service.ts file and inject HttpClient via the service constructor:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class ApiService {

  constructor(private httpClient: HttpClient) { }
}

Sending GET Request for Fetching Data

Next, define an API_KEY variable which will hold your API key from the News API:

export class ApiService {
  API_KEY = 'YOUR_API_KEY';

Finally, add a method that sends a GET request to an endpoint for TechCrunch news:

  public getNews(){
    return this.httpClient.get(`https://newsapi.org/v2/top-headlines?sources=techcrunch&apiKey=${this.API_KEY}`);
  }

That’s all we need to add for the service.

How the <strong>HttpClient.get()</strong> Method Works

The HttpClient get() method is designed to send HTTP GET requests. The syntax is as follows:

get(url: string, options: {
      headers?: HttpHeaders;
      observe: 'response';
      params?: HttpParams;
      reportProgress?: boolean;
      responseType?: 'json';
      withCredentials?: boolean;
}): Observable<HttpResponse<Object>>;

It takes a REST API endpoint and an optional options object and returns an Observable instance.

Creating an Angular 8 Component

Now, let's create an Angular 8 component for displaying the news data. Head back to your terminal and run the following command:

$ ng generate component news

Injecting ApiService in Your Component

Next, open the src/app/news/news.component.ts file and start by importing ApiService in your component:

import { ApiService } from '../api.service';

Next, you need to inject ApiService via the component's constructor:

import { Component, OnInit } from '@angular/core';
import { ApiService } from '../api.service';
@Component({
  selector: 'app-news',
  templateUrl: './news.component.html',
  styleUrls: ['./news.component.css']
})
export class NewsComponent implements OnInit {

  constructor(private apiService: ApiService) { }
}

Sending the GET Request & Subscribing to The Observable

Next, define an articles variable and call the getNews() method of the API service in the ngOnInit()method of the component:

export class NewsComponent implements OnInit {
  articles;

  constructor(private apiService: ApiService) { }
  ngOnInit() {
    this.apiService.getNews().subscribe((data)=>{
      console.log(data);
      this.articles = data['articles'];
    });
  }
}

This will make sure our data is fetched once the component is loaded.

We call the getNews() method and subscribe to the returned Observable which will send a GET request to the news endpoint.

Displaying Data in The Template with NgFor

Let’s now display the news articles in our component template. Open the src/app/news.component.htmlfile and update it as follows:

<div *ngFor="let article of articles">
  <h2>{{article.title}}</h2>

    <p>
      {{article.description}}
    </p>
    <a href="{{article.url}}">Read full article</a>
</div>

Adding the Angular Component to The Router

Angular CLI 8 has automatically added routing for us, so we don’t need to set up anything besides adding the component(s) to our Router configuration. Open the src/app/app-routing.module.ts file and start by importing the news component as follows:

import { NewsComponent } from './news/news.component';

Next, add the component to the routes array:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { NewsComponent } from './news/news.component';
const routes: Routes = [
  {path:'news', component: NewsComponent}
];
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

You can now access your component from the /news path.

Conclusion

In this tutorial, we used Angular 8 to build a simple news application that retrieves data from a JSON REST API using the get() method of HttpClient. We’ve seen how to subscribe to the RxJS Observable returned by the get() method and how to use the *ngFor directive to iterate over fetched data in the template. Finally, we’ve seen how we can create an Angular 8 project using Angular CLI v8, how to generate components and services and how to configure routing for the component.

Angular 8 Forms Tutorial - Reactive Forms Validation Example

Angular 8 Forms Tutorial - Reactive Forms Validation Example

In this article, you''ll see a quick example of how to setup form validation in Angular 8 using Reactive Forms.

In this article, you''ll see a quick example of how to setup form validation in Angular 8 using Reactive Forms.

The example is a simple registration form with pretty standard fields for title, first name, last name, email, password, confirm password and an accept Ts & Cs checkbox. All fields are required including the checkbox, the email field must be a valid email address and the password field must have a min length of 6. There's also a custom validator called MustMatch which is used to validate that the confirm password and password fields match.

I've setup the form to validate on submit rather than as soon as each field is changed, this is implemented with a submitted property in the app component that is set to true when the form is submitted for the first time, and reset to false if the cancel button is clicked.

Styling of the example is all done with Bootstrap 4.3 CSS.

See on StackBlitz at https://stackblitz.com/edit/angular-8-reactive-form-validation

Reactive Forms Validation App Component

The app component defines the form fields and validators for our registration form using an Angular FormBuilder to create an instance of a FormGroup that is stored in the registerForm property. The registerForm is then bound to the form in the app template below using the [formGroup] directive.

I also added a getter f as a convenience property to make it easier to access form controls from the template. So for example you can access the confirmPassword field in the template using f.confirmPassword instead of registerForm.controls.confirmPassword.

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

// import custom validator to validate that password and confirm password fields match
import { MustMatch } from './_helpers/must-match.validator';

@Component({ selector: 'app', templateUrl: 'app.component.html' })
export class AppComponent implements OnInit {
    registerForm: FormGroup;
    submitted = false;

    constructor(private formBuilder: FormBuilder) { }

    ngOnInit() {
        this.registerForm = this.formBuilder.group({
            title: ['', Validators.required],
            firstName: ['', Validators.required],
            lastName: ['', Validators.required],
            email: ['', [Validators.required, Validators.email]],
            password: ['', [Validators.required, Validators.minLength(6)]],
            confirmPassword: ['', Validators.required],
            acceptTerms: [false, Validators.requiredTrue]
        }, {
            validator: MustMatch('password', 'confirmPassword')
        });
    }

    // convenience getter for easy access to form fields
    get f() { return this.registerForm.controls; }

    onSubmit() {
        this.submitted = true;

        // stop here if form is invalid
        if (this.registerForm.invalid) {
            return;
        }

        // display form values on success
        alert('SUCCESS!! :-)\n\n' + JSON.stringify(this.registerForm.value, null, 4));
    }

    onReset() {
        this.submitted = false;
        this.registerForm.reset();
    }
}
Reactive Forms Validation App Template

The app component template contains all the html markup for displaying the example registration form in your browser. The form element uses the [formGroup] directive to bind to the registerForm FormGroup in the app component above.

The form binds the form submit event to the onSubmit() handler in the app component using the Angular event binding (ngSubmit)="onSubmit()". Validation messages are displayed only after the user attempts to submit the form for the first time, this is controlled with the submitted property of the app component.

The cancel button click event is bound to the onReset() handler in the app component using the Angular event binding (click)="onReset()".



    ##### Angular 8 Reactive Form Validation

    
        
            
                
                    Title
                    
                        
                        Mr
                        Mrs
                        Miss
                        Ms
                    
                    
                        Title is required

                    
                
                
                    First Name
                    
                    
                        First Name is required

                    
                
                
                    Last Name
                    
                    
                        Last Name is required

                    
                
            
            
                Email
                
                
                    Email is required

                    Email must be a valid email address

                
            
            
                
                    Password
                    
                    
                        Password is required

                        Password must be at least 6 characters

                    
                
                
                    Confirm Password
                    
                    
                        Confirm Password is required

                        Passwords must match

                    
                
            
            
                
                Accept Terms & Conditions
                Accept Ts & Cs is required

            
            
                Register
                Cancel
            
        
    

Reactive Forms Custom "Must Match" Validator

The custom MustMatch validator is used in this example to validate that both of the password fields - password and confirmPassword - are matching. However it can be used to validate that any pair of fields is matching (e.g. email and confirm email fields).

It works slightly differently than a typical custom validator because I'm setting the error on the second field instead of returning it to be set on the formGroup. I did it this way because I think it makes the template a bit cleaner and more intuitive, the mustMatch validation error is displayed below the confirmPassword field so I think it makes sense that the error is attached the the confirmPassword form control.

import { FormGroup } from '@angular/forms';

// custom validator to check that two fields match
export function MustMatch(controlName: string, matchingControlName: string) {
    return (formGroup: FormGroup) => {
        const control = formGroup.controls[controlName];
        const matchingControl = formGroup.controls[matchingControlName];

        if (matchingControl.errors && !matchingControl.errors.mustMatch) {
            // return if another validator has already found an error on the matchingControl
            return;
        }

        // set error on matchingControl if validation fails
        if (control.value !== matchingControl.value) {
            matchingControl.setErrors({ mustMatch: true });
        } else {
            matchingControl.setErrors(null);
        }
    }
}
Reactive Forms Validation App Module

There isn't much going on in the app module other than the standard stuff, the main thing you need to remember for using reactive forms in Angular is to import the ReactiveFormsModule from '@angular/forms' and include it in the imports array of the @NgModule decorator.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';

import { AppComponent } from './app.component';

@NgModule({
    imports: [
        BrowserModule,
        ReactiveFormsModule
    ],
    declarations: [
        AppComponent
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }

Angular 8 Pagination Example and Tutorial

Angular 8 Pagination Example and Tutorial

Pagination is the best way to show huge number of records in series for any application. Also showing/fetching thousands of record at one go will affect the performance of the application.

Pagination is the best way to show huge number of records in series for any application. Also showing/fetching thousands of record at one go will affect the performance of the application.

For example, when you search something that returns a large number of records which cannot be shown on a single web page therefore, those records are part into number of pages that can be accessed through links via pagination structure.

So today in this demo we will discuss the simple pagination in Angular 8.

Step 1: Create a basic app with angular cli
ng new angular8-simple-pagination-example

By typing the above command we will see a basic angular app created on the current folder. So move to the created folder by typing **cd angular8-simple-pagination-example/. **You can check the newly created app by typing http://localhost:4200 on the browser.

Step 2: install ngx-pagination pagination dependency from terminal

So run the below command over terminal

npm install ngx-pagination --save

Step 3: Create dummy records for pagination

Now we will create static data to show the pagination. So lets have a look on the code under file **app.component.ts **

import { Component } from '@angular/core';
import {NgxPaginationModule} from 'ngx-pagination';
@Component({
   selector: 'app-root',
   templateUrl: './app.component.html',
   styleUrls: ['./app.component.css']
})
export class AppComponent {
   title = 'simple pagination demo';
   collection = [];
   constructor(){
     for(let i=1;i<=100;i++){
       let Obj = {'name': `Employee Name ${i}`,'code': `EMP00 ${i}`}
       this.collection.push(Obj);
     }
   }
}

In the above file, we can see that inside constructor we have created a loop for created dummy record for 100 employees having employee name & code for showing pagination.

Step 4: Import dependency in app.module.ts

Now let's have a look on the code inside **app.module.ts **where the ngx-pagination module has been imported

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
 
import { NgxPaginationModule } from 'ngx-pagination';
import { AppComponent } from './app.component';
 
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
NgxPaginationModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

Step 5: Update view from app.component.html

Now one last step needed to do is, add the below code anywhere inside app.component.html

*  Emp Name | Emp code
 {{item.name}} | {{item.code}} 


Now, we are done with all the needed steps for the pagination in our angular application.

Step 6: Run the app

Run the app over the terminal with npm start and check the app after typing the url http://localhost:4200/.** **A page will open like below:

Conclusion

By following these easy steps we can easily achieve the client side pagination in Angular 8 application. If you want to impliment server side pagination in angular8 Server Side Pagination in Angular Example and Tutorial . You can also find other demos of Angular Sample Application here to start working on enterprise level application. Click here to view more about the pagination package over npm.

Create Simple Login Page using Angular 8 and HTTP Authentication

Create Simple Login Page using Angular 8 and HTTP Authentication

In this article, you'll learn how to setup a simple login page using Angular 8 and Basic HTTP authentication

In this article, you'll learn how to setup a simple login page using Angular 8 and Basic HTTP authentication

Tutorial built with Angular 8.0.2 and the Angular CLI

Angular CLI was used to generate the base project structure with the ng new command, the CLI is also used to build and serve the application. For more info about the Angular CLI see https://angular.io/cli.

Styling of the example app is all done with Bootstrap 4.3, for more info about Bootstrap see https://getbootstrap.com/docs/4.3/getting-started/introduction/.

Running the Angular 8 Basic Authentication Tutorial Example Locally
  1. Install NodeJS and NPM from https://nodejs.org/en/download/.
  2. Download or clone the tutorial project source code from https://github.com/cornflourblue/angular-8-basic-authentication-example
  3. Install all required npm packages by running npm install from the command line in the project root folder (where the package.json is located).
  4. Start the application by running npm start from the
  5. command line in the project root folder, this will build the application
  6. and automatically launch it in the browser on the URL
  7. http://localhost:4200.

NOTE: You can also run the app directly using the Angular CLI command ng serve --open. To do this first install the Angular CLI globally on your system with the command npm install -g @angular/cli.

Running the Tutorial Example with a Real Backend API

The Angular 8 basic authentication example app uses a fake / mock backend by default so it can run in the browser without a real api, to switch to a real backend api you just have to remove or comment out the line below the comment // provider used to create fake backend located in the /src/app/app.module.ts file.

Angular 8 Tutorial Project Structure

The app and code structure of the tutorial mostly follows the best practice recommendations in the official Angular Style Guide, with a few of my own tweaks here and there.

Each feature has it's own folder (home & login), other shared/common code such as services, models, helpers etc are placed in folders prefixed with an underscore _ to easily differentiate them and group them together at the top of the folder structure.

The index.ts files in each folder are barrel files that group the exported modules from a folder together so they can be imported using the folder path instead of the full module path and to enable importing multiple modules in a single import (e.g. import { AuthenticationService, UserService } from '../_services').

Path aliases @app and @environments have been configured in tsconfig.json that map to the /src/app and /src/environments directories. This allows imports to be relative to the app and environments folders by prefixing import paths with aliases instead of having to use long relative paths (e.g. import MyComponent from '../../../MyComponent').

Here are the main project files that contain the application logic, I left out some files that were generated by Angular CLI ng new command that I didn't change.

  • src
  • app
  • _helpers
  • auth.guard.ts
  • basic-auth.interceptor.ts
  • error.interceptor.ts
  • fake-backend.ts
  • index.ts
  • _models
  • user.ts
  • index.ts
  • _services
  • authentication.service.ts
  • user.service.ts
  • index.ts
  • home
  • home.component.html
  • home.component.ts
  • index.ts
  • login
  • login.component.html
  • login.component.ts
  • index.ts
  • app.component.html
  • app.component.ts
  • app.module.ts
  • app.routing.ts
  • environments
  • environment.prod.ts
  • environment.ts
  • index.html
  • main.ts
  • polyfills.ts
  • styles.less
  • package.json
  • tsconfig.json
Auth Guard

Path: /src/app/_helpers/auth.guard.ts

The auth guard is an angular route guard that's used to prevent unauthenticated users from accessing restricted routes, it does this by implementing the CanActivate interface which allows the guard to decide if a route can be activated with the canActivate() method. If the method returns true the route is activated (allowed to proceed), otherwise if the method returns false the route is blocked.

The auth guard uses the authentication service to check if the user is logged in, if they are logged in it returns true from the canActivate() method, otherwise it returns false and redirects the user to the login page.

Angular route guards are attached to routes in the router config, this auth guard is used in app.routing.ts to protect the home page route.

import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';

import { AuthenticationService } from '@app/_services';

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
    constructor(
        private router: Router,
        private authenticationService: AuthenticationService
    ) { }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        const currentUser = this.authenticationService.currentUserValue;
        if (currentUser) {
            // logged in so return true
            return true;
        }

        // not logged in so redirect to login page with the return url
        this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
        return false;
    }
}
Basic Authentication Interceptor

Path: /src/app/_helpers/basic-auth.interceptor.ts

The Basic Authentication Interceptor intercepts http requests from the application to add basic authentication credentials to the Authorization header if the user is logged in.

It's implemented using the HttpInterceptor class included in the HttpClientModule, by extending the HttpInterceptor class you can create a custom interceptor to modify http requests before they get sent to the server.

Http interceptors are added to the request pipeline in the providers section of the _app.module.ts_ file.

import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable } from 'rxjs';

import { AuthenticationService } from '@app/_services';

@Injectable()
export class BasicAuthInterceptor implements HttpInterceptor {
    constructor(private authenticationService: AuthenticationService) { }

    intercept(request: HttpRequest, next: HttpHandler): Observable> {
        // add authorization header with basic auth credentials if available
        const currentUser = this.authenticationService.currentUserValue;
        if (currentUser && currentUser.authdata) {
            request = request.clone({
                setHeaders: { 
                    Authorization: `Basic ${currentUser.authdata}`
                }
            });
        }

        return next.handle(request);
    }
}
Http Error Interceptor

Path: /src/app/_helpers/error.interceptor.ts

The Error Interceptor intercepts http responses from the api to check if there were any errors. If there is a 401 Unauthorized response the user is automatically logged out of the application, all other errors are re-thrown up to the calling service so an alert with the error can be displayed on the screen.

It's implemented using the HttpInterceptor class included in the HttpClientModule, by extending the HttpInterceptor class you can create a custom interceptor to catch all error responses from the server in a single location.

Http interceptors are added to the request pipeline in the providers section of the app.module.ts file.

import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

import { AuthenticationService } from '@app/_services';

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
    constructor(private authenticationService: AuthenticationService) { }

    intercept(request: HttpRequest, next: HttpHandler): Observable> {
        return next.handle(request).pipe(catchError(err => {
            if (err.status === 401) {
                // auto logout if 401 response returned from api
                this.authenticationService.logout();
                location.reload(true);
            }

            const error = err.error.message || err.statusText;
            return throwError(error);
        }))
    }
}
Fake Backend Provider

Path: /src/app/_helpers/fake-backend.ts

In order to run and test the Angular application without a real backend API, the example uses a fake backend that intercepts the HTTP requests from the Angular app and send back "fake" responses. This is done by a class that implements the Angular HttpInterceptor interface, for more information on Angular HTTP Interceptors see https://angular.io/api/common/http/HttpInterceptor

The fake backend contains a handleRoute function that checks if the request matches one of the faked routes in the switch statement, at the moment this includes POST requests to the /users/authenticate route for handling authentication, and GET requests to the /users route for getting all users.

Requests to the authenticate route are handled by the authenticate() function which checks the username and password against an array of hardcoded users. If the username and password are correct then an ok response is returned with the user details, otherwise an error response is returned.

Requests to the get users route are handled by the getUsers() function which checks if the user is logged in by calling the new isLoggedIn() helper function. If the user is logged in an ok() response with the whole users array is returned, otherwise a 401 Unauthorized response is returned by calling the new unauthorized() helper function.

If the request doesn't match any of the faked routes it is passed through as a real HTTP request to the backend API.

import { Injectable } from '@angular/core';
import { HttpRequest, HttpResponse, HttpHandler, HttpEvent, HttpInterceptor, HTTP_INTERCEPTORS } from '@angular/common/http';
import { Observable, of, throwError } from 'rxjs';
import { delay, mergeMap, materialize, dematerialize } from 'rxjs/operators';

import { User } from '@app/_models';

const users: User[] = [{ id: 1, username: 'test', password: 'test', firstName: 'Test', lastName: 'User' }];

@Injectable()
export class FakeBackendInterceptor implements HttpInterceptor {
    intercept(request: HttpRequest, next: HttpHandler): Observable> {
        const { url, method, headers, body } = request;

        // wrap in delayed observable to simulate server api call
        return of(null)
            .pipe(mergeMap(handleRoute))
            .pipe(materialize()) // call materialize and dematerialize to ensure delay even if an error is thrown (https://github.com/Reactive-Extensions/RxJS/issues/648)
            .pipe(delay(500))
            .pipe(dematerialize());

        function handleRoute() {
            switch (true) {
                case url.endsWith('/users/authenticate') && method === 'POST':
                    return authenticate();
                case url.endsWith('/users') && method === 'GET':
                    return getUsers();
                default:
                    // pass through any requests not handled above
                    return next.handle(request);
            }    
        }

        // route functions

        function authenticate() {
            const { username, password } = body;
            const user = users.find(x => x.username === username && x.password === password);
            if (!user) return error('Username or password is incorrect');
            return ok({
                id: user.id,
                username: user.username,
                firstName: user.firstName,
                lastName: user.lastName
            })
        }

        function getUsers() {
            if (!isLoggedIn()) return unauthorized();
            return ok(users);
        }

        // helper functions

        function ok(body?) {
            return of(new HttpResponse({ status: 200, body }))
        }

        function error(message) {
            return throwError({ error: { message } });
        }

        function unauthorized() {
            return throwError({ status: 401, error: { message: 'Unauthorised' } });
        }

        function isLoggedIn() {
            return headers.get('Authorization') === `Basic ${window.btoa('test:test')}`;
        }
    }
}

export let fakeBackendProvider = {
    // use fake backend in place of Http service for backend-less development
    provide: HTTP_INTERCEPTORS,
    useClass: FakeBackendInterceptor,
    multi: true
};
User Model

Path: /src/app/_models/user.ts

The user model is a small class that defines the properties of a user.

export class User {
    id: number;
    username: string;
    password: string;
    firstName: string;
    lastName: string;
    authdata?: string;
}
Authentication Service

Path: /src/app/_services/authentication.service.ts

The authentication service is used to login & logout of the Angular app, it notifies other components when the user logs in & out, and allows access the currently logged in user.

RxJS Subjects and Observables are used to store the current user object and notify other components when the user logs in and out of the app. Angular components can subscribe() to the public currentUser: Observable property to be notified of changes, and notifications are sent when the this.currentUserSubject.next() method is called in the login() and logout() methods, passing the argument to each subscriber. The RxJS BehaviorSubject is a special type of Subject that keeps hold of the current value and emits it to any new subscribers as soon as they subscribe, while regular Subjects don't store the current value and only emit values that are published after a subscription is created.

The login() method sends the user credentials to the API via an HTTP POST request for authentication. If successful the user's basic authentication data (base64 encoded username and password) is added to the user object and stored in localStorage to keep the user logged in between page refreshes. The user object is then published to all subscribers with the call to this.currentUserSubject.next(user);.

The basic auth data is used by the basic authentication interceptor above to set the authorization header of http requests made to secure api endpoints.

The constructor() of the service initialises the currentUserSubject with the currentUser object from localStorage which enables the user to stay logged in between page refreshes or after the browser is closed. The public currentUser property is then set to this.currentUserSubject.asObservable(); which allows other components to subscribe to the currentUser Observable but doesn't allow them to publish to the currentUserSubject, this is so logging in and out of the app can only be done via the authentication service.

The currentUserValue getter allows other components an easy way to get the value of the currently logged in user without having to subscribe to the currentUser Observable.

The logout() method removes the current user object from local storage and publishes null to the currentUserSubject to notify all subscribers that the user has logged out.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import { environment } from '@environments/environment';
import { User } from '@app/_models';

@Injectable({ providedIn: 'root' })
export class AuthenticationService {
    private currentUserSubject: BehaviorSubject;
    public currentUser: Observable;

    constructor(private http: HttpClient) {
        this.currentUserSubject = new BehaviorSubject(JSON.parse(localStorage.getItem('currentUser')));
        this.currentUser = this.currentUserSubject.asObservable();
    }

    public get currentUserValue(): User {
        return this.currentUserSubject.value;
    }

    login(username: string, password: string) {
        return this.http.post(`${environment.apiUrl}/users/authenticate`, { username, password })
            .pipe(map(user => {
                // store user details and basic auth credentials in local storage to keep user logged in between page refreshes
                user.authdata = window.btoa(username + ':' + password);
                localStorage.setItem('currentUser', JSON.stringify(user));
                this.currentUserSubject.next(user);
                return user;
            }));
    }

    logout() {
        // remove user from local storage to log user out
        localStorage.removeItem('currentUser');
        this.currentUserSubject.next(null);
    }
}
User Service

Path: /src/app/_services/user.service.ts

The user service contains a method for getting all users from the api, I included it to demonstrate accessing a secure api endpoint with the http authorization header set after logging in to the application, the auth header is automatically set with basic authentication credentials by the basic authentication interceptor. The secure endpoint in the example is a fake one implemented in the fake backend provider.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { environment } from '@environments/environment';
import { User } from '@app/_models';

@Injectable({ providedIn: 'root' })
export class UserService {
    constructor(private http: HttpClient) { }

    getAll() {
        return this.http.get(`${environment.apiUrl}/users`);
    }
}
Home Component Template

Path: /src/app/home/home.component.html

The home component template contains html and angular 8 template syntax for displaying a simple welcome message and a list of users from a secure api endpoint.


    #### You're logged in with Angular 8 & Basic HTTP Authentication!!

    
        ###### Users from secure api end point

        

        
            {{user.firstName}} {{user.lastName}}
        
    

Home Component

Path: /src/app/home/home.component.ts

The home component defines an angular 8 component that gets all users from the user service and makes them available to the template via a users array property.

import { Component } from '@angular/core';
import { first } from 'rxjs/operators';

import { User } from '@app/_models';
import { UserService } from '@app/_services';

@Component({ templateUrl: 'home.component.html' })
export class HomeComponent {
    loading = false;
    users: User[];

    constructor(private userService: UserService) { }

    ngOnInit() {
        this.loading = true;
        this.userService.getAll().pipe(first()).subscribe(users => {
            this.loading = false;
            this.users = users;
        });
    }
}
Login Component Template

Path: /src/app/login/login.component.html

The login component template contains a login form with username and password fields. It displays validation messages for invalid fields when the submit button is clicked. The form submit event is bound to the onSubmit() method of the login component.


    
        Username: test

        Password: test
    
    
        #### Angular 8 Basic Auth Login Example

        
            
                
                    Username
                    
                    
                        Username is required

                    
                
                
                    Password
                    
                    
                        Password is required

                    
                
                
                    
                    Login
                
                {{error}}

            
        
    

Login Component

Path: /src/app/login/login.component.ts

The login component uses the authentication service to login to the application. If the user is already logged in they are automatically redirected to the home page.

The loginForm: FormGroup object defines the form controls and validators, and is used to access data entered into the form. The FormGroup is part of the Angular Reactive Forms module and is bound to the login template above with the [formGroup]="loginForm" directive.

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { first } from 'rxjs/operators';

import { AuthenticationService } from '@app/_services';

@Component({ templateUrl: 'login.component.html' })
export class LoginComponent implements OnInit {
    loginForm: FormGroup;
    loading = false;
    submitted = false;
    returnUrl: string;
    error = '';

    constructor(
        private formBuilder: FormBuilder,
        private route: ActivatedRoute,
        private router: Router,
        private authenticationService: AuthenticationService
    ) { 
        // redirect to home if already logged in
        if (this.authenticationService.currentUserValue) { 
            this.router.navigate(['/']);
        }
    }

    ngOnInit() {
        this.loginForm = this.formBuilder.group({
            username: ['', Validators.required],
            password: ['', Validators.required]
        });

        // get return url from route parameters or default to '/'
        this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
    }

    // convenience getter for easy access to form fields
    get f() { return this.loginForm.controls; }

    onSubmit() {
        this.submitted = true;

        // stop here if form is invalid
        if (this.loginForm.invalid) {
            return;
        }

        this.loading = true;
        this.authenticationService.login(this.f.username.value, this.f.password.value)
            .pipe(first())
            .subscribe(
                data => {
                    this.router.navigate([this.returnUrl]);
                },
                error => {
                    this.error = error;
                    this.loading = false;
                });
    }
}
App Component Template

Path: /src/app/app.component.html

The app component template is the root component template of the application, it contains the main nav bar which is only displayed for authenticated users, and a router-outlet directive for displaying the contents of each view based on the current route / path.



    
        Home
        Logout
    




    

App Component

Path: /src/app/app.component.ts

The app component is the root component of the application, it defines the root tag of the app as `` with the selector property of the @Component() decorator.

It subscribes to the currentUser observable in the authentication service so it can reactively show/hide the main navigation bar when the user logs in/out of the application. I didn't worry about unsubscribing from the observable here because it's the root component of the application, the only time the component will be destroyed is when the application is closed which would destroy any subscriptions as well.

The app component contains a logout() method which is called from the logout link in the main nav bar above to log the user out and redirect them to the login page.

import { Component } from '@angular/core';
import { Router } from '@angular/router';

import { AuthenticationService } from './_services';
import { User } from './_models';

@Component({ selector: 'app', templateUrl: 'app.component.html' })
export class AppComponent {
    currentUser: User;

    constructor(
        private router: Router,
        private authenticationService: AuthenticationService
    ) {
        this.authenticationService.currentUser.subscribe(x => this.currentUser = x);
    }

    logout() {
        this.authenticationService.logout();
        this.router.navigate(['/login']);
    }
}
App Module

Path: /src/app/app.module.ts

The app module defines the root module of the application along with metadata about the module. For more info about angular 8 modules check out this page on the official docs site.

This is where the fake backend provider is added to the application, to switch to a real backend simply remove the providers located below the comment // provider used to create fake backend.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';

// used to create fake backend
import { fakeBackendProvider } from './_helpers';

import { AppComponent } from './app.component';
import { appRoutingModule } from './app.routing';

import { BasicAuthInterceptor, ErrorInterceptor } from './_helpers';
import { HomeComponent } from './home';
import { LoginComponent } from './login';

@NgModule({
    imports: [
        BrowserModule,
        ReactiveFormsModule,
        HttpClientModule,
        appRoutingModule
    ],
    declarations: [
        AppComponent,
        HomeComponent,
        LoginComponent
    ],
    providers: [
        { provide: HTTP_INTERCEPTORS, useClass: BasicAuthInterceptor, multi: true },
        { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },

        // provider used to create fake backend
        fakeBackendProvider
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }
App Routing Module

Path: /src/app/app.routing.ts

Routing for the Angular app is configured as an array of Routes, each component is mapped to a path so the Angular Router knows which component to display based on the URL in the browser address bar. The home route is secured by passing the AuthGuard to the canActivate property of the route.

The Routes array is passed to the RouterModule.forRoot() method which creates a routing module with all of the app routes configured, and also includes all of the Angular Router providers and directives such as the `` directive. For more information on Angular Routing and Navigation see https://angular.io/guide/router.

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

import { HomeComponent } from './home';
import { LoginComponent } from './login';
import { AuthGuard } from './_helpers';

const routes: Routes = [
    { path: '', component: HomeComponent, canActivate: [AuthGuard] },
    { path: 'login', component: LoginComponent },

    // otherwise redirect to home
    { path: '**', redirectTo: '' }
];

export const appRoutingModule = RouterModule.forRoot(routes);
Production Environment Config

Path: /src/environments/environment.prod.ts

The production environment config contains variables required to run the application in production. This enables you to build the application with a different configuration for each different environment (e.g. production & development) without updating the app code.

When you build the application for production with the command ng build --prod, the output environment.ts is replaced with environment.prod.ts.

export const environment = {
    production: true,
    apiUrl: 'http://localhost:4000'
};
Development Environment Config

Path: /src/environments/environment.ts

The development environment config contains variables required to run the application in development.

Environment config is accessed by importing the environment object into any Angular service of component with the line import { environment } from '@environments/environment' and accessing properties on the environment object, see the user service for an example.

export const environment = {
    production: false,
    apiUrl: 'http://localhost:4000'
};
Main Index Html File

Path: /src/index.html

The main index.html file is the initial page loaded by the browser that kicks everything off. The Angular CLI (with Webpack under the hood) bundles all of the compiled javascript files together and injects them into the body of the index.html page so the scripts can be loaded and executed by the browser.




    
    Angular 8 - Basic HTTP Authentication Tutorial & Example
    

    
    


    Loading...


Main (Bootstrap) File

Path: /src/main.ts

The main file is the entry point used by angular to launch and bootstrap the application.

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
    enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
    .catch(err => console.error(err));
Polyfills

Path: /src/polyfills.ts

Some features used by Angular 8 are not yet supported natively by all major browsers, polyfills are used to add support for features where necessary so your Angular 8 application works across all major browsers.

This file is generated by the Angular CLI when creating a new project with the ng new command, I've excluded the comments in the file for brevity.

import 'zone.js/dist/zone';
Global LESS/CSS Styles

Path: /src/styles.less

The global styles file contains LESS/CSS styles that are applied globally throughout the application.

/* You can add global styles to this file, and also import other style files */
a { cursor: pointer }
npm package.json

Path: /package.json

The package.json file contains project configuration information including package dependencies which get installed when you run npm install. Full documentation is available on the npm docs website.

{
    "name": "angular-8-basic-authentication-example",
    "version": "1.0.0",
    "scripts": {
        "ng": "ng",
        "start": "ng serve --open",
        "build": "ng build",
        "test": "ng test",
        "lint": "ng lint",
        "e2e": "ng e2e"
    },
    "private": true,
    "dependencies": {
        "@angular/animations": "~8.0.1",
        "@angular/common": "~8.0.1",
        "@angular/compiler": "~8.0.1",
        "@angular/core": "~8.0.1",
        "@angular/forms": "~8.0.1",
        "@angular/platform-browser": "~8.0.1",
        "@angular/platform-browser-dynamic": "~8.0.1",
        "@angular/router": "~8.0.1",
        "rxjs": "~6.4.0",
        "tslib": "^1.9.0",
        "zone.js": "~0.9.1"
    },
    "devDependencies": {
        "@angular-devkit/build-angular": "~0.800.0",
        "@angular/cli": "~8.0.3",
        "@angular/compiler-cli": "~8.0.1",
        "@angular/language-service": "~8.0.1",
        "@types/node": "~8.9.4",
        "@types/jasmine": "~3.3.8",
        "@types/jasminewd2": "~2.0.3",
        "codelyzer": "^5.0.0",
        "jasmine-core": "~3.4.0",
        "jasmine-spec-reporter": "~4.2.1",
        "karma": "~4.1.0",
        "karma-chrome-launcher": "~2.2.0",
        "karma-coverage-istanbul-reporter": "~2.0.1",
        "karma-jasmine": "~2.0.1",
        "karma-jasmine-html-reporter": "^1.4.0",
        "protractor": "~5.4.0",
        "ts-node": "~7.0.0",
        "tslint": "~5.15.0",
        "typescript": "~3.4.3"
    }
}
TypeScript tsconfig.json

Path: /tsconfig.json

The tsconfig.json file configures how the TypeScript compiler will convert TypeScript into JavaScript that is understood by the browser. More information is available on the TypeScript docs.

Most of the file is unchanged from when it was generated by the Angular CLI, only the paths property has been added to map @app and @environments to the /src/app and /src/environments directories. This allows imports to be relative to the app and environments folders by prefixing import paths with aliases instead of having to use long relative paths (e.g. import MyComponent from '../../../MyComponent').

{
    "compileOnSave": false,
    "compilerOptions": {
        "baseUrl": "./",
        "outDir": "./dist/out-tsc",
        "sourceMap": true,
        "declaration": false,
        "downlevelIteration": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "module": "esnext",
        "moduleResolution": "node",
        "importHelpers": true,
        "target": "es2015",
        "typeRoots": [
            "node_modules/@types"
        ],
        "lib": [
            "es2018",
            "dom"
        ],
        "paths": {
            "@app/*": ["src/app/*"],
            "@environments/*": ["src/environments/*"]
        }
    }
}

The tutorial code is available on GitHub