The magic of RXJS sharing operators and their differences

The magic of RXJS sharing operators and their differences

The magic of RXJS sharing operators and their differences

Before diving into sharing operators first we need to determinate what kind of observables are out there in RxJs. There are usually two kind of observables, hot and cold. There is a great article Hot vs Cold Observables, but in general the main difference is that

Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.
For example interval() creates cold observable. Data is created within the observable and for each new subscription new interval will be created
Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.
For example fromEvent() creates hot observable as notification producer is in DOM and exists regardless of number of subscribers.

Sometimes we need to make cold observable behave as hot, for example with http requests. Consider following http request example in Angular

ngOnInit() {
   this.user$ = this.http.get(`api/user/1`)

   this.name$ = this.user$.pipe(
      map(user => user.name)
   );

   this.age$ = this.user$.pipe(
      map(user => user.age)
   );

}

and we are displaying user’s name and age in the template using async pipe (assume in different places so wrapping it in one async pipe is not possible)

<div>{{name$ | async}}</div>
<div>{{age$ | async}}</div>

In browser’s network tab we will see two requests. The reason is that Angular’s Http creates cold observable so each new subscriber is equal to new request. We definitely don’t want to have several requests. First let’s solve this issue and then see how it works.

Solving this actually is really easy. All we need to do is to add share() or publish(), refCount() like so

this.user$ = this.http.get(`api/user/1`).pipe(
  share()
);

//or 

this.user$ = this.http.get(`api/user/1`).pipe(
 publish(),
 refCount()
);

And now in network tab we have one request because the data was shared among all subscribers. So how does share() or publish() magically solve this problem and what is the difference between them if they both do the same thing ?

Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.
In order to understand how sharing operators work we need to understand what is multicasting.
Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.#### multicast()

RxJs has multicast() operator which takes subject or subject factory and returns ConnectableObservable . The subject passed as argument acts like a middleman in a multicast observable. It just relays data from source observable to all subscribers. ConnectableObservable is a regular observable, but it won’t subscribe to the source until connect() method is called. Let’s change the example above with multicast() to understand how it works.

this.user$ = this.http.get(`api/user/1`).pipe(
  multicast(new Subject())
);

This alone won’t work because we need to manually call connect()

this.name$ = this.user$.pipe(
      map(user => user.name)
   );

this.age$ = this.user$.pipe(
      map(user => user.age)
   );

this.user$.connect();

After this we gonna see same behavior, there will be only one http call instead of two. Connecting and disconnecting manually might be hard to implement, therefore there is a refCount() operator that will automatically connect() with first subscription, keep count of subscriptions and keep Subject connected to the Source as long as there is at least one subscriber. When S_ubscriptions_ count drops to zero, Subject will be disconnected from the Source.

Source observable in our example is observable returned by this.http.get()

Subject is the internal subject passed as argument to multicast()

Subscriptions or observers are this.name$ and this.age$ and other observers that are subscribed to Subject.

In simple words all our subscribers will subscribe to subject X (passed to multicast) and subject X itself will subscribe to our http call. When observable returned by http call emits, our subject X will take that value and share among all subscribers.

Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.

Now let’s modify our code to use refCount(), so we don’t need to manually connect.

this.user$ = this.http.get(`api/user/1`).pipe(
  multicast(new Subject()),
  refCount()
);

Now we don’t need to manually call connect() and worry about disconnecting. refCount() will connect Subject to the Source upon first subscription to it and will disconnect when there are no observers anymore. 

In fact earlier we used combination of publish(), refCount() and that is totally the same as multicast(new Subject()), refCount()

publish()

RxJs has publish() operator and if we look at source code we will see that it uses multicast with Subject()

Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.
publish() optionally accepts selector function and in fact it changes the behavior of the operator and deserves its own article. We will skip that part and consider using publish() without selector function.

So when we use publish() we actually use the old multicast() with Subject() and so we either need to manually carry about connecting and disconnecting, or use refCount() to automate that process.

Because we mostly use publish() with refCount() there is very similar operator which uses refCount() internally and behaves similarly . That is share()

share()

Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.
In our first example we saw that share() does the same thing as publish(), refCount() and in most cases they are the same. share() is an operator that uses refCount() internally, so we don’t need to call it. share() just like publish() uses multicast() but the difference is in the argument passed to multicast().
Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.> Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.
That’s the only reason why we can’t say that share() is the same as publish() + refCount() . This difference cause different behavior for late subscribers when Source has completed.

Differences between share() and publish() + refCount()

They both use refCount() for managing subscriptions however

Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.> Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.
The difference is subtle but very important. Let’s modify our code to have a button which will update user’s data. When clicking it will re-fetch the data from server. 

First let’s use share()

ngOnInit() {
   this.user$ = this.http.get(`api/user/1`).pipe(
      share()
   )

this.name$ = this.user$.pipe(
      map(user => user.name)
   );

this.age$ = this.user$.pipe(
      map(user => user.age)
   );

}

update() {
  this.name$ = this.user$.pipe(
    map(user => user.name + 'update')
  );
  this.age$ = this.user$.pipe(
    map(user => user.age + 'updated')
  );
}

When we initially load the data refCount() will count all references. So we will have two references to Subject. Once we get data from server Subject will get that data from Source and complete. Both our subscribers will get the data from Subject and complete too, which means reference number in refCount() will be 0. In this case Subject will be disconnected from Source. 

When we execute update() method, new Subject() instance will be created and subscribed to Source. So each execution of update() will actually send request to the server.

Now consider same example with publish(), refCount()

this.user$ = this.http.get(`api/user/1`).pipe(
 publish(),
 refCount()
);

Again we will have refCount() counter set to 2 and once Source emits and completes counter will be 0. But when we execute update() method nothing will happen, no requests will be made to the server. As written above new subscribers will only get ‘complete’ notifications if the Source is completed.

The reason why they behave so is in the multicast(). Because publish() uses Subject instance, when Source completes, Subject will complete too, so any new subscriber to that Subject will receive only ‘complete’ notification.

share() uses factory function which returns Subject instance. When Source completes, Subject will complete too, but for new subscribers new Subject instance will be created and subscribed to Source.

multicast() with different subject type

Until now we discussed about multicast using Subject. There are few other types of Subject — ReplaySubject, BehaviorSubject and AsyncSubject. Passing different subject to multicast will return ConnectableObservable, but their behavior will differ.

First let’s look at ReplaySubject(n) , it takes number as argument which is the count of emits it will keep in buffer. For any new subscriber it will replay n-emits.

If we pass ReplaySubject(n) to multicast() all new subscribers will get n replayed values.

publishReplay()

Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.
publishReplay() returns ConnectableObservable so we either need to use connect() or use refCount() for managing connections. Let’s modify our example so that every new subscriber will just get buffered value. So when we click update() we won’t receive new data, but get cached value.

this.user$ = this.http.get(`api/user/1`).pipe(
 publishReplay(1),
 refCount()
);

All subscribers to ReplaySubject before completion of Source will get emitted values (in our case just 1 value as Http emits only once). For all new subscribers ReplaySubject will replay N buffered values.

Because we mostly use publishReplay() with refCount(), there is very similar operator which uses reference counting mechanism internally and behaves similarly. That is shareReplay()

shareReplay()

shareReplay() is very interesting operator. It can behave similar way as publishReplay() + refCount() but it depends on how we use this operator.

Before RxJs version 6.4.0 the reference counting mechanism in shareReplay() worked in a different way. Starting from 6.4.0 we can explicitly pass an argument to shareReplay() to use ‘normal’ reference counting mechanism. Let’s see in more detail

shareReplay({refCount: true}) (RXJS 6.4.0 or newer)

refCount: true tells shareReplay() to use reference counting mechanism, similar to refCount() . In this case shareReplay({refCount: true}) is almost the same as publishReplay() + refCount() . Let’s modify our example to use shareReplay.

this.user$ = this.http.get(`api/user/1`).pipe(
 shareReplay({refCount: true, bufferSize: 1})
);

As you can see we don’t use refCount() anymore because shareReplay({refCount: true}) uses its own reference counting mechanism .

Result will be the same. All subscribers to ReplaySubject will get values as long as it emits them. All new subscribers will get N buffered values.

Before talking about other ways of using shareReplay, let’s see their differences.

Differences between shareReplay({refCount: true}) and publishReplay() + refCount()

They both use ReplaySubject() but shareReplay() is not implemented with multicast() .

Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.> Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.
To see the difference let’s use interval() so that for new subscribers Source will not be completed

this.source = interval(1000).pipe(
  publishReplay(1),
  refCount()
);
const sub1 = this.source.subscribe(x => console.log('sub 1', x));
const sub2 = this.source.subscribe(x => console.log('sub 2', x));

setTimeout(() => {
  sub1.unsubscribe();
  sub2.unsubscribe();
}, 2000);

We have 2 subscription to ReplaySubject, sub1 and sub2. After 2 seconds both of them will unsubscribe from Subject. Because we are using refCount() when there will be no more subscribers (e.g. reference count drops to zero) it will disconnect ReplaySubject from Source. So far in the console we will see.

  sub 1– 0
 sub 2– 0
 sub 1– 1
 sub 2– 1

Now suppose we will create new subscriber to ReplaySubject with a click of button. (after refCount drops to zero)

newSub() {
 const sub3 = this.source.subscribe(x => console.log(‘sub 3’, x));
}

When newSub() gets executed sub3 will get last buffered value from ReplaySubject (which will be 1) and check is Source has completed. If completed, sub3 will receive ‘completed’ notification and complete as well. However because we are using interval(), Source won’t be completed and internal ReplaySubject will re-subscribe to Source again. The result will be

 sub 1– 0
 sub 2– 0
 sub 1– 1
 sub 2– 1
/**** execution of newSub() ****/
 sub 3– 1 <- replayed value
 sub 3– 0 <- new subscription
 sub 3– 1
 sub 3– 2
...

Internal ReplaySubject replays buffered values to new observers and either completes, or re-subscribes to Source depending on the status of Source completion.

Now same interval() example using shareReplay({refCount:true })

this.source = interval(1000).pipe(
  shareReplay({refCount: true, bufferSize: 1})
);
const sub1 = this.source.subscribe(x => console.log('sub 1', x));
const sub2 = this.source.subscribe(x => console.log('sub 2', x));

setTimeout(() => {
  sub1.unsubscribe();
  sub2.unsubscribe();
}, 2000);

//execute newSub() after sub1 and sub2 unsubscribe
newSub() {
 const sub3 = this.source.subscribe(x => console.log(‘sub 3’, x));
}

shareReplay() is not implemented with multicast but it uses a factory function internally and if we use it with refCount: true and ref count drops to zero, for any new subscriber if Source has completed it will replay buffered values and emit ‘completed’ notification. If Source has not completed for any new subscriber new ReplaySubject will be created and subscribed to Source. 

As a result after running code above and executing newSub() we will see.

 sub 1– 0
 sub 2– 0
 sub 1– 1
 sub 2– 1
/**** execution of newSub() ****/
 sub 3– 0 <- new subscription
 sub 3– 1
 sub 3– 2
...

As you can see no value was replayed for sub3. The reason is that when sub1 and sub2 unsubscribed, ref count will be zero and if Source has completed all new subscribers like sub3 will get all buffered values and ‘completed’ notification, but as we are using interval() and Source won’t be completed, ReplaySubject will be destroyed and any new subscriber like sub3 will create new ReplaySubject instance and subscribe to Source again.

shareReplay() without refCount

Till now we used shareReplay with refCount: true . We can use shareReplay with refCount set to false or not set at all and specify only buffer size — for example shareReplay({refCount: false, bufferSize: 3}) and shareReplay(3) are the same. This means that ReplaySubject will emit last 3 values and refCount will be false. This doesn’t mean that there will be no reference counting mechanism, it will just behave differently.

Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.
refCount: false means that upon first subscription to ReplaySubject it will subscribe to Source. But it won’t disconnect ReplaySubject from Source where there are no more subscribers to ReplaySubject. Let’s modify our example again using refCount false

this.source = interval(1000).pipe(
  shareReplay({refCount: false, bufferSize: 2})
  //or just shareReplay(2)
);
const sub1 = this.source.subscribe(x => console.log('sub 1', x));
const sub2 = this.source.subscribe(x => console.log('sub 2', x));

setTimeout(() => {
  sub1.unsubscribe();
  sub2.unsubscribe();
}, 2000);

setTimeout(() => {
 const sub3 = this.source.subscribe(x => console.log(‘sub 3’, x));
}, 4000);

sub1 and sub2 subscribe to ReplaySubject and ReplaySubject subscribes to Source interval. After 2 second sub1 and sub2 will unsubscribe, but in this case ReplaySubject will NOT unsubscribe from Source. Source will continue to emit values even though there are no subscribers to catch theses values. 

After 4 seconds new subscriber will subscribe to ReplaySubject and will get last 2 buffered values and continue getting values from Source. The result will be

sub 1– 0
 sub 2– 0
 sub 1– 1
 sub 2– 1
/**** after 4 seconds  ****/
 sub 3– 2 <- replayed values
 sub 3– 3 <-
 sub 3– 4 <- continues receiving values
 sub 3– 5
...

sub1 and sub2 subscribed, printed values and after two seconds unsubscribed, but because Source has not completed yet, ReplaySubject will receive data from Source and so when after 4 seconds sub3 subscribes to ReplaySubject it will get not the 0 and 1 as buffered values, but 2 and 3,becausein that time ReplaySubject managed to get new values from Source and update its buffer. The only case where ReplaySubject will unsubscribe from Source is when Source completes or errors. Any new subscriber in that case will get replayed values and complete.

Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.#### publishBehavior()

publishBehavior() uses multicast with another subject. BehaviorSubject

Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.
publishBehavior() returns ConnectableObservable so we need to use refCount() or connect manually.

publishBehavior() accepts default value as parameter and will emit that value to all subscribers if Source hasn’t emitted. Consider this example

this.source = interval(1000).pipe(
 publishBehavior(47),
  refCount()
);
const sub1 = this.source.subscribe(x => console.log('sub 1', x));
const sub2 = this.source.subscribe(x => console.log('sub 2', x));

setTimeout(() => {
  sub1.unsubscribe();
  sub2.unsubscribe();
}, 2000);

setTimeout(() => {
  const sub3 = this.source.subscribe(x => console.log('sub 3', x));
}, 4000);

The result will be

 sub 1– 47 <- default values
 sub 2– 47 <-
 sub 1– 0
 sub 2– 0
 sub 1– 1
 sub 2– 1
/**** after 4 seconds ****/
 sub 3– 1 <- last buffered value
 sub 3– 1
 sub 3– 2
...

Because interval is async, when sub1 and sub2 subscribe to BehaviorSubject, at that time Source hasn’t emitted yet, so sub1 and sub2 will get default values from BehaviorSubject. After two seconds sub1 and sub2 will unsubscribe from BehaviorSubject and BehaviorSubject itself will unsubscribe from Source. After 4 seconds sub3 will subscribe to BehaviorSubject and because Source has not completed yet, sub3 will get the last emitted value and re-subscribe to Source using same BehaviorSubject.

Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.#### publishLast()

publishLast() uses multicast with AsyncSubject

Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.
As with all multicast operators publishLast() is best used with refCount() . 

AsyncSubject used in this operator is very interesting. It won’t emit values if subscribed until completes after which emits the last value.

this.source = interval(1000).pipe(
 take(2),
 publishLast(),
 refCount()
);

const sub1 = this.source.subscribe(x => console.log('sub 1', x));
const sub2 = this.source.subscribe(x => console.log('sub 2', x));

setTimeout(() => {
  const sub3 = this.source.subscribe(x => console.log('sub 3', x));
}, 7000);

Because interval is infinite observable we used take(2) so it will emit 2 values and complete. This is the result

sub 1– 1 //completed
 sub 2– 1 //completed
/**** after 7 seconds ****/
 sub 3– 1 //completed

when sub1 and sub2 subscribe to AsyncSubject they will not receive any values until Source completes. When Source completes AsyncSubject will pass last value to all observers and complete too. After 7 seconds sub3 subscribes to AsyncSubject and because it is completed it will pass last value and ‘completed’ notification to sub3 too.

Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.> Notification producer in cold observables is created by the observable itself and only when observer subscribers to it.
There is a lot going on in Rxjs and multicasting is on of most important things in the library. Hope this article helped you to understand how sharing operators work and what is their difference. Thanks for reading.

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

Introduction New Features in TypeScript 3.7 and How to Use Them

Introduction New Features in TypeScript 3.7 and How to Use Them

The TypeScript 3.7 release is coming soon, and it's going to be a big one.

The target release date is November 5th, and there are some seriously exciting headline features included:

  • Assert signatures.
  • Recursive type aliases.
  • Top-level await.
  • Null coalescing.
  • Optional chaining.

Personally, I'm super excited about this, they're going to whisk away all sorts of annoyances that I've been fighting in TypeScript while building HTTP Toolkit.

If you haven't been paying close attention to the TypeScript development process though, it's probably not clear what half of these mean, or why you should care. Let's talk through all of them.

Assert Signatures

This is a brand-new and little-known TypeScript feature, which allows you to write functions that act like type guards as a side-effect, rather than explicitly returning their boolean result.

It's easiest to demonstrate this with a JavaScript example:

function assertString(input) { 
  if (typeof input === 'string') 
    return; 
  else 
    throw new Error('Input must be a string!'); 
} 
function doSomething(input) { 
  assertString(input); 
  // ... Use input, confident that it's a string 
} 
doSomething('abc'); 
// All good doSomething(123); // Throws an error

This pattern is neat and useful, and you can't use it in TypeScript today.

TypeScript can't know that you've guaranteed the type of input after it's run assertString. Typically, people just make the argument input: string to avoid this, and that's good. But, it also just pushes the type checking problem somewhere else, and in cases where you just want to fail hard, it's useful to have this option available.

Fortunately, soon we will:

// With TS 3.7 
function assertString(input: any): 
	asserts input is string { 
      // <-- the magic 
      if (typeof input === 'string') 
        return; 
      else 
        throw new Error('Input must be a string!'); 
    } 
function doSomething(input: string | number) { 
  assertString(input); 
  // input's type is just 'string' here }

Here assert input is string means that if this function ever returns, TypeScript can narrow the type of input to string, just as if it was inside an if block with a type guard.

To make this safe, that means if the assert statement isn't true then your assert function must either throw an error or not return at all (kill the process, infinite loop, you name it).

That's the basics, but this actually lets you pull some really neat tricks:

// With TS 3.7 
// Asserts that input is truthy, throwing immediately if not: 
function assert(input: any): 
	asserts input { // <-- not a typo 
      if (!input) 
        throw new Error('Not a truthy value'); 
    } 
declare const x: number | string | undefined; 
assert(x); // Narrows x to number | string 
// Also usable with type guarding expressions! 
assert(typeof x === 'string'); 
// Narrows x to string // -- Or use assert in your tests: -- 
const a: Result | Error = doSomethingTestable(); 
expect(a).is.instanceOf(result); 
// 'instanceOf' could 'asserts a is Result' 
expect(a.resultValue).to.equal(123); 
// a.resultValue is now legal // -- Use as a safer ! that throws immediately if 
// you're wrong -- 
function assertDefined<T>(obj: T): 
	asserts obj is NonNullable<T> { 
      if (obj === undefined || obj === null) { 
        throw new Error('Must not be a nullable value'); 
      } 
    } 
declare const x: string | undefined; 
// Gives y just 'string' as a type, could throw elsewhere later: 
const y = x!; 
// Gives y 'string' as a type, or throws immediately if you're wrong: 
assertDefined(x); const z = x; 
// -- Or even update types to track a function's side-effects -- 
type X<T extends string | {}> = { value: T }; 
// Use asserts to narrow types according to side effects: 
function setX<T extends string | {}>(x: X<any>, v: T): 
	asserts x is X<T> { 
      x.value = v; 
    } 
	declare let x: X<any>; 
// x is now { value: any }; 
setX(x, 123); 
// x is now { value: number };

This is still in flux, so don't take it as the definite result, and keep an eye on the pull request if you want the final details.

There's even a discussion there about allowing functions to assert something and return a type, which would let you extend the final example above to track a much wider variety of side effects, but we'll have to wait and see how that plays out.

Top-Level Await

Async/await is amazing and makes promises dramatically cleaner to use.

Unfortunately, though, you can't use them at the top level. This might not be something you care about much in a TS library or application, but if you're writing a runnable script or using TypeScript in a REPL, then this gets super annoying.

It's even worse if you're used to frontend development, since top-level await has been working nicely in the Chrome and Firefox console for a couple of years now.

Fortunately though, a fix is coming. This is actually a general stage-3 JS proposal, so it'll be everywhere else eventually too, but for TS devs 3.7 is where the magic happens.

This one's simple, but let's have another quick demo anyway:


// Your only solution right now for a script that does something async: 
async function doEverything() { 
  ... 
  const response = await fetch('http://example.com'); 
  ... 
} 
  
doEverything(); // <- eugh (could use an IIFE instead, but even more eugh)

With top-level await:

// With TS 3.7: 
// Your script: ... 
const response = await fetch('http://example.com'); 
// ...

There's a notable gotcha here: if you're not writing a script, or using a REPL, don't write this at the top level, unless you really know what you're doing!

It's totally possible to use this to write modules that do blocking async steps when imported. That can be useful for some niche cases, but people tend to assume that their import statement is a synchronous, reliable, and fairly quick operation, and you could easily hose your codebase's startup time if you start blocking imports for complex async processes (even worse, processes that can fail).

This is somewhat mitigated by the semantics of imports of async modules: they're imported and run in parallel, so the importing module effectively waits for Promise.all(importedModules) before being executed.

Rich Harris wrote an excellent piece on a previous version of this spec before that change when imports ran sequentially and this problem was much worse), which makes for good background reading on the risks here if you're interested.

It's also worth noting that this is only useful for module systems that support asynchronous imports. There isn't yet a formal spec for how TS will handle this, but that likely means that a very recent target configuration, and, either ES Modules or Webpack v5 (whose alphas have experimental support), will be used at runtime.

Recursive Type Aliases

If you're ever tried to define a recursive type in TypeScript, you may have run into StackOverflow questions like this: https://stackoverflow.com/questions/47842266/recursive-types-in-typescript.

Right now, you can't. Interfaces can be recursive, but there are limitations to their expressiveness, and type aliases can't. That means right now, you need to combine the two: define a type alias and extract the recursive parts of the type into interfaces. It works, but it's messy, and we can do better.

As a concrete example, this is the suggested type definition for JSON data:

type JSONValue = | string | number | boolean | JSONObject | JSONArray; 
interface JSONObject { [x: string]: JSONValue; } 
interface JSONArray extends Array<JSONValue> { }

That works, but the extra interfaces are only there because they're required to get around the recursion limitation.

Fixing this requires no new syntax; it just removes that restriction, so the below compiles:

// With TS 3.7: 
type JSONValue = | string | number | boolean | { [x: string]: JSONValue } | Array<JSONValue>;

Right now, that fails to compile with Type alias 'JSONValue' circularly references itself. Soon though, soon...

Null Coalescing

Aside from being difficult to spell, this one is quite simple and easy. It's based on a JavaScript stage-3 proposal, which means it'll also be coming to your favorite vanilla JavaScript environment soon (if it hasn't already).

In JavaScript, there's a common pattern for handling default values, and falling back to the first valid result of a defined group. It looks something like this:

// Use the first of firstResult/secondResult which is truthy: 
const result = firstResult || secondResult; 
// Use configValue from provided options if truthy, or 'default' if not: 
this.configValue = options.configValue || 'default';

This is useful in a host of cases, but due to some interesting quirks in JavaScript, it can catch you out. If firstResult or options.configValue can meaningfully be set to false, an empty string or 0, then this code has a bug. If those values are set, then when considered as booleans they're falsy, so the fallback value (secondResult / 'default') is used anyway.

Null coalescing fixes this. Instead of the above, you'll be able to write:

// With TS 3.7: 
// Use the first of firstResult/secondResult which is *defined*: 
const result = firstResult ?? secondResult; 
// Use configSetting from provided options if *defined*, or 'default' if not: 
this.configValue = options.configValue ?? 'default';

?? differs from || in that it falls through to the next value only if the first argument is null or undefined, not falsy. That fixes our bug. If you pass false as firstResult, that will be used instead of secondResult because, while it's falsy, it is still defined, and that's all that's required.

It's simple but super-useful, as it takes away a whole class of bugs.

Optional Chaining

Last but not least, optional chaining is another stage-3 proposal that is making its way into TypeScript.

This is designed to solve an issue faced by developers in every language: how do you get data out of a data structure when some or all of it might not be present?

Right now, you might do something like this:

// To get data.key1.key2, if any level could be null/undefined: 
let result = data ? (data.key1 ? data.key1.key2 : undefined) : undefined; 
// Another equivalent alternative: 
let result = ((data || {}).key1 || {}).key2;

Nasty! This gets much much worse if you need to go deeper, and although the second example works at runtime, it won't even compile in TypeScript, since the first step could be {}, in which case key1 isn't a valid key at all.

This gets still more complicated if you're trying to get into an array, or there's a function call somewhere in this process.

There's a host of other approaches to this, but they're all noisy, messy & error-prone. With optional chaining, you can do this:

// With TS 3.7: 
// Returns the value is it's all defined & non-null, or undefined if not. 
let result = data?.key1?.key2; 
// The same, through an array index or property, if possible: 
array?.[0]?.['key']; 
// Call a method, but only if it's defined: 
obj.method?.(); 
// Get a property, or return 'default' if any step is not defined: 
let result = data?.key1?.key2 ?? 'default';

The last case shows how neatly some of these dovetails together: null coalescing + optional chaining is a match made in heaven.

One gotcha: this will return undefined for missing values, even if they were null, e.g. in cases like (null)?.key (returns undefined). A small point, but one to watch out for if you have a lot of null in your data structures.

That's the lot! That should outline all the essentials for these features, but there are lots of smaller improvements, fixes, and editor support improvements coming too, so take a look at the official roadmap if you want to get into the nitty-gritty.