Building a Chat application with Angular 8, SignalR and ASP.NET Core

Building a Chat application with Angular 8, SignalR and ASP.NET Core

In this post, we are going to create a simple chat application using Angular 8, ASP.NET Core and SignalR

In this post, we are going to create a simple chat application using Angular 8, ASP.NET Core 2.2.0, and SignalR 1.1.0 as shown below.

We need to install all the below-mentioned prerequisites on our development machine.

Prerequisite

  • .NET Core SDK 2.2.0 – download from here.
  • Node.js 10.15.3 – download from here.
  • Angular CLI 8.0 – Install Angular command package by executing this command: “npm install -g @angular/[email protected]
Set up ASP.NET Core with SignalR Project

Open the command prompt and enter the following command to create an ASP.NET Core web project as below.

Install the following NuGet package in your project.

  • Microsoft.AspNetCore.SignalR
  • Microsoft.AspNetCore.SpaServices.Extensions

Call SignalR in the “Startup.ConfigureServices” method to add SignalR services.

services.AddSignalR();

Create MessageHub Class

SignalR makes real-time client-to-server and server-to-client communications possible and it will call methods to connect clients from a server using SignalR Hub API. You can find more details about SignalR from here. Now, we need to perform the following steps to create the MessageHub class that is extending SignalR Hub API features.

  • Create Hubs Folder in the project
  • Declare MessageHub class that inherits from SignalR Hub
  • Add the NewMessage public method to get a new message from a connected client.
  • Then, Send an async message to all connected clients.
using ChatApp.Models;  
using Microsoft.AspNetCore.SignalR;  
using System.Threading.Tasks;  
  
namespace ChatApp.Hubs  
{  
    public class MessageHub : Hub  
    {  
        public async Task NewMessage(Message msg)  
        {  
            await Clients.All.SendAsync("MessageReceived", msg);  
        }  
    }  
}  

Then, add route to handle a request in Startup.ConfigureService method for MessageHub.

app.UseSignalR(options =>  
{  
      options.MapHub<MessageHub>("/MessageHub");  
 });  

Add a Message Class

public class Message  
   {  
       public string clientuniqueid { get; set; }  
       public string type { get; set; }  
       public string message { get; set; }  
       public DateTime date { get; set; }  
   }  
Add Angular 8 into the project

We will scaffold Angular into our project. For this, we need to execute “ng new ClientApp --skip-install” on Visual Studio Code terminal. Here, --skip-install option is used to skip installation of the npm packages.

Now, let us enter the “npm install” command to install all Angular npm packages in the terminal. And then, update the SPA static file service configuration to the Angular output files folder location. For this, we will add the below code to the “Startup.Configure” method.

app.UseSpa(spa =>  
            {  
                // To learn more about options for serving an Angular SPA from ASP.NET Core,  
                // see https://go.microsoft.com/fwlink/?linkid=864501  
  
                spa.Options.SourcePath = "ClientApp";  
  
                if (env.IsDevelopment())  
                {  
                    //spa.UseProxyToSpaDevelopmentServer("http://localhost:4200");  
                    spa.UseAngularCliServer(npmScript: "start");  
                }  
            });  

Here, UseAngularCliServer will pass the request through to an instance of Angular CLI server which keeps up-to-date CLI-built resources without having to run the Angular CLI server manually.

Now, add SignalR client library to connect MessageHub from the Angular as below screenshot.

Create Chat Service to connect SignalR

Create chat.service.ts class to establish a connection with Message Hub and to publish & receive chat messages.

Then, we have added chat service as a provider in Ng Modules.

import { EventEmitter, Injectable } from '@angular/core';  
import { HubConnection, HubConnectionBuilder } from '@aspnet/signalr';  
import { Message } from '../models/message';  
  
@Injectable()  
export class ChatService {  
  messageReceived = new EventEmitter<Message>();  
  connectionEstablished = new EventEmitter<Boolean>();  
  
  private connectionIsEstablished = false;  
  private _hubConnection: HubConnection;  
  
  constructor() {  
    this.createConnection();  
    this.registerOnServerEvents();  
    this.startConnection();  
  }  
  
  sendMessage(message: Message) {  
    this._hubConnection.invoke('NewMessage', message);  
  }  
  
  private createConnection() {  
    this._hubConnection = new HubConnectionBuilder()  
      .withUrl(window.location.href + 'MessageHub')  
      .build();  
  }  
  
  private startConnection(): void {  
    this._hubConnection  
      .start()  
      .then(() => {  
        this.connectionIsEstablished = true;  
        console.log('Hub connection started');  
        this.connectionEstablished.emit(true);  
      })  
      .catch(err => {  
        console.log('Error while establishing connection, retrying...');  
        setTimeout(function () { this.startConnection(); }, 5000);  
      });  
  }  
  
  private registerOnServerEvents(): void {  
    this._hubConnection.on('MessageReceived', (data: any) => {  
      this.messageReceived.emit(data);  
    });  
  }  
}    
Create a Chat App Component

app.component.html:

<div class="container">  
  <h3 class=" text-center chat_header">Chat Application</h3>  
  <div class="messaging">  
    <div class="inbox_msg">  
      <div class="mesgs">  
        <div class="msg_history">  
          <div *ngFor="let msg of messages">  
          <div class="incoming_msg" *ngIf="msg.type == 'received'">  
            <div class="incoming_msg_img"> </div>  
            <div class="received_msg">  
              <div class="received_withd_msg">  
                <p>  
                 {{msg.message}}   
                </p>  
                <span class="time_date"> {{msg.date | date:'medium'}} </span>  
              </div>  
            </div>  
          </div>  
          <div class="outgoing_msg" *ngIf="msg.type == 'sent'">  
            <div class="sent_msg">  
              <p>  
                  {{msg.message}}   
              </p>  
              <span class="time_date"> {{msg.date | date:'medium'}}</span>  
            </div>  
          </div>  
        </div>  
        </div>  
        <div class="type_msg">  
          <div class="input_msg_write">  
            <input type="text" class="write_msg" [value]="txtMessage"  
            (input)="txtMessage=$event.target.value" (keydown.enter)="sendMessage()" placeholder="Type a message" />  
            <button class="msg_send_btn" type="button"  (click)="sendMessage()"><i class="fa fa-paper-plane-o" aria-hidden="true"></i></button>  
          </div>  
        </div>  
      </div>  
    </div>  
  
  </div>  
</div>  

app.component.ts :

import { Component, NgZone } from '@angular/core';  
import { Message } from '../models/Message';  
import { ChatService } from '../services/chat.service';  
  
@Component({  
  selector: 'app-root',  
  templateUrl: './app.component.html',  
  styleUrls: ['./app.component.css']  
})  
export class AppComponent {  
  
  title = 'ClientApp';  
  txtMessage: string = '';  
  uniqueID: string = new Date().getTime().toString();  
  messages = new Array<Message>();  
  message = new Message();  
  constructor(  
    private chatService: ChatService,  
    private _ngZone: NgZone  
  ) {  
    this.subscribeToEvents();  
  }  
  sendMessage(): void {  
    if (this.txtMessage) {  
      this.message = new Message();  
      this.message.clientuniqueid = this.uniqueID;  
      this.message.type = "sent";  
      this.message.message = this.txtMessage;  
      this.message.date = new Date();  
      this.messages.push(this.message);  
      this.chatService.sendMessage(this.message);  
      this.txtMessage = '';  
    }  
  }  
  private subscribeToEvents(): void {  
  
    this.chatService.messageReceived.subscribe((message: Message) => {  
      this._ngZone.run(() => {  
        if (message.clientuniqueid !== this.uniqueID) {  
          message.type = "received";  
          this.messages.push(message);  
        }  
      });  
    });  
  }  
}  

Now, our chat application is ready. Let's run the following command in terminal and test apps.

dotnet run  
Summary

In this article, we have learned how we can create a sample chat app using Angular 8 and ASP.NET Core with SignalR.

Please find the entire source code here as an attachment and also on GitHub.

Thanks!

Angular and ASP.NET Core

Angular and ASP.NET Core

​ The&nbsp;[Angular CLI](https://cli.angular.io/ "Angular CLI")&nbsp;provides a way to develop front-end applications using angular that hides a lot of details. For example there's no requirement to understand how&nbsp;[Webpack](https://webpack.js.org/ "Webpack")&nbsp;or&nbsp;[SystemJS](https://github.com/systemjs/systemjs "SystemJS")&nbsp;work. ​ In fact, if you don't know a little bit about Webpack, which is what is used to build the latest version of Angular applications, the CLI almost looks like magic. You just need to do a&nbsp;ng new&nbsp;and&nbsp;ng serve --open&nbsp;and you have a working Angular application open in your web browser. ​ The fact that the CLI hides all the plumbing might lead to questions like: "How do I use Angular with ASP.NET Core?". ​ ![](https://res.cloudinary.com/practicaldev/image/fetch/s--dKBafg3O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://www.blinkingcaret.com/wp-content/uploads/2018/01/asp_net_core_and_angular_logo.png) ​ I hope that by the end of this blog post it will be clear to you how you can answer that question (and not only with ASP.NET Core, with whichever technology you want to use your Angular app with). ​ You see, an angular app is an app in and of itself, it does need to be "served" somehow by a web server. ​ When you compile an angular application you are producing a set of JavaScript, CSS and one index.html file. That's it. ​ The default folder where those "artifacts" get copied to is&nbsp;yourApplicationFolder/dist. You can check it out by going to your Angular application and doing an&nbsp;ng build. ​ Go on, I'll wait. ​ When you do&nbsp;ng serve --open&nbsp;you are actually using a stand-alone web server ([webpack-dev-server](https://github.com/webpack/webpack-dev-server "webpack-dev-server")) to serve that index.html file in the dist folder. ​ The rest of this blog post will describe several approaches that you can take for using Angular with ASP.NET Core. The first is to have ASP.NET Core serve the Angular files. ​ The second approach is to have Angular and ASP.NET Core as different applications. There's an example of how to achieve this using Nginx where both Angular and ASP.NET Core are served using port 80 and in IIS where each application is served from its own port. ​ The final part of the post describes a setup that I consider ideal where you can use Angular's&nbsp;ng serve&nbsp;during development. ​ This post is quite long but the sections are fairly independent. If your are only interested in the last section and you are using Windows I recommend also reading the section on how to configure Angular in IIS. ## Using ASP.NET Core to serve the Angular application ​ It can be argued that serving an Angular application "within" ASP.NET Core is wasteful in terms of resources. In the end the Angular application is just a set of static files, there's no need to have the request for those files go through the ASP.NET Core middleware pipeline. ​ There might be some good reasons for doing it though, also there's no harm in knowing how to do it and since it seems to be a common approach, being familiar with it might be useful. ​ One important thing to know in order to understand how we can serve an ASP.NET Core and Angular application together is to understand how a request is processed in ASP.NET Core. ​ When you run an ASP.NET Core application your request goes through a "pipeline" of&nbsp;[middlewares](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware?tabs=aspnetcore2x "middlewares"). Every time a request comes in it goes through the middlewares in the order they are defined, and then in reverse order. ​ Every middleware has an opportunity to change the request or response two times, once before the other middlewares have been executed, and then after the other middlewares have executed. This allows for a middleware at the top of the pipeline to handle for example, a 401 response set by a middleware further down in the pipeline. ​ An example of this are the authentication middlewares that change a 401 response to a 302 redirect to a login page. The Angular CLI provides a way to develop front-end applications using angular that hides a lot of details. For example there’s no requirement to understand how Webpack or SystemJS work.

The Angular CLI provides a way to develop front-end applications using angular that hides a lot of details. For example there's no requirement to understand how Webpack or SystemJS work.

In fact, if you don't know a little bit about Webpack, which is what is used to build the latest version of Angular applications, the CLI almost looks like magic. You just need to do a ng new and ng serve --open and you have a working Angular application open in your web browser.

The fact that the CLI hides all the plumbing might lead to questions like: "How do I use Angular with ASP.NET Core?".

I hope that by the end of this blog post it will be clear to you how you can answer that question (and not only with ASP.NET Core, with whichever technology you want to use your Angular app with).

You see, an angular app is an app in and of itself, it does need to be "served" somehow by a web server.

When you compile an angular application you are producing a set of JavaScript, CSS and one index.html file. That's it.

The default folder where those "artifacts" get copied to is yourApplicationFolder/dist. You can check it out by going to your Angular application and doing an ng build.

Go on, I'll wait.

When you do ng serve --open you are actually using a stand-alone web server (webpack-dev-server) to serve that index.html file in the dist folder.

The rest of this blog post will describe several approaches that you can take for using Angular with ASP.NET Core. The first is to have ASP.NET Core serve the Angular files.

The second approach is to have Angular and ASP.NET Core as different applications. There's an example of how to achieve this using Nginx where both Angular and ASP.NET Core are served using port 80 and in IIS where each application is served from its own port.

The final part of the post describes a setup that I consider ideal where you can use Angular's ng serve during development.

This post is quite long but the sections are fairly independent. If your are only interested in the last section and you are using Windows I recommend also reading the section on how to configure Angular in IIS.

Using ASP.NET Core to serve the Angular application

It can be argued that serving an Angular application "within" ASP.NET Core is wasteful in terms of resources. In the end the Angular application is just a set of static files, there's no need to have the request for those files go through the ASP.NET Core middleware pipeline.

There might be some good reasons for doing it though, also there's no harm in knowing how to do it and since it seems to be a common approach, being familiar with it might be useful.

One important thing to know in order to understand how we can serve an ASP.NET Core and Angular application together is to understand how a request is processed in ASP.NET Core.

When you run an ASP.NET Core application your request goes through a "pipeline" of middlewares. Every time a request comes in it goes through the middlewares in the order they are defined, and then in reverse order.

Every middleware has an opportunity to change the request or response two times, once before the other middlewares have been executed, and then after the other middlewares have executed. This allows for a middleware at the top of the pipeline to handle for example, a 401 response set by a middleware further down in the pipeline.

An example of this are the authentication middlewares that change a 401 response to a 302 redirect to a login page.

You can find the definition of this pipeline on the Startup.cs file, in the Configure method. For example, here's the pipeline that you get when you do a dotnet new mvc:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

Every time a request comes in to this ASP.NET Core application it can go through at most three middlewares. First the DeveloperExceptionPage/ExceptionHandler middleware depending if the ASP.NET Core application is running in development mode or not. Then the StaticFiles middleware and then finally the Mvc middleware.

The middleware that is key here is StaticFiles. This is the middleware that serves files contained in the wwwroot folder, i.e. if a request comes in for index.html and there's an index.html file at wwwroot/index.html then that file is sent to the client. StaticFiles middleware won't call the middlewares below it after this (in this case it would be Mvc).

You can probably already see how this could work with an Angular application. Just put it under wwwroot.

That's absolutely correct, however there's a detail about StaticFiles that is important to know. StaticFiles won't try to do any guesses for you, i.e. if your request is for /, StaticFiles won't look for /index.html. It will just assume that this request isn't supposed to be handled by it and it will call the next middleware in the pipeline, in this case Mvc.

For this approach to work you need another middleware named DefaultFiles which must come before StaticFiles in the pipeline:

//...
app.UseDefaultFiles();
app.UseStaticFiles();
//...

DefaultFiles will cause cause StaticFiles to look for index.html if the url ends with /.

Now the only thing left to do is to configure your Angular CLI to compile to your ASP.NET Core application's wwwroot folder.

If you look in your Angular's application folder you'll find a .angular-cli.json file. In that file look for the outDir property:

...
"apps": [
{
    ...
    "outDir": "dist",
...

Change it from "dist" to the path of your ASP.NET Core's wwwroot folder. Run ng build in your Angular application and now if you run your ASP.NET Core web application you should see your Angular application in the browser.

A nice development workflow is to run the Angular CLI build in watch mode: In a console window do ng build --watch or ng build -w if you want to save a few key strokes, and leave it running. Now every time you make a change in your Angular application you can just refresh the browser and see the change (you also need to have your ASP.NET Core application running).

There is one thing missing from this approach, though. Deep-linking support, i.e. if your Angular application uses routing and you send a user a url with a valid Angular route (e.g http://yourapplication.com/products/5) the receiving user won't be able to open it. Trying to get to that route will result in a 404 Not Found response.

That's because the request will go all the way through your ASP.NET Core application's pipeline and when it reaches the MVC middleware it won't know what to do with it and will set the response's status code to 404 Page Not Found.

What we can do is at the top of the pipeline we look for a 404 response that is about to be sent and change its path to our Angular application's index.html file (that way what gets served is the Angular application which will know what to do with the url in terms of routing). After this we make the request go through the pipeline again:

//add this at the start of Configure
app.Use(async (HttpContext context, Func<Task> next) =>
{
    await next.Invoke();

    if (context.Response.StatusCode == 404)
    {
        context.Request.Path = new PathString("/index.html");
        await next.Invoke();
    }
});

That fixes deep links but introduces a new problem. What if your web api (that you've implemented in your ASP.NET Core application) needs to send a 404 response. That's something more than reasonable to do. Instead of a 404, the service call will receive a 200 response with index.html.

The solution here is to look at the url and decide if it's intended for the web api or an Angular route. Usually a call to the web api will have /api in the url. That's a simple test to perform and it will solve this problem. Here's the revised version of a custom middleware that solves this problem:

//add this at the start of Configure
app.Use(async (HttpContext context, Func<Task> next) =>
{
    await next.Invoke();

    if (context.Response.StatusCode == 404 && !context.Request.Path.Value.Contains("/api")))
    {
        context.Request.Path = new PathString("/index.html");
        await next.Invoke();
    }
});

One last note about this approach. I've seen examples where the Angular application is in the same Visual Studio solution as the ASP.NET application. Visual Studio (not VS Code) will try to compile the typescript files. If you are using ng build -w you'll want Visual Studio to leave your Typescript files alone. To do that open your project's .csproj and add in any PropertyGroup:

<TypescriptCompileBlocked>true</TypescriptCompileBlocked>

Nginx

Nginx is a web server that can act as a reverse proxy for ASP.NET Core applications and which is also very good at serving static content.

The setup for having an Angular application work with ASP.NET Core is much simpler in Nginx. You just need a configuration similar to this:

server {
    listen 80;        

    location / {
        root /pathToYourAngularApplication/dist;
        index index.html;
        try_files $uri $uri/ /index.html;
    }

    location /api/ {
        proxy_pass http://localhost:5000;
    }
}

This is how a typical Nginx configuration file looks like. If you are not familiar with Nginx and ASP.NET Core I recommend my blog post: HTTPS in ASP.NET Core from Scratch. It has a section with instructions on how to install and setup websites using Nginx.

This configuration allows us to have both the Angular and ASP.NET Core application on port 80. Let's look at the important parts in it.

The listen 80 statement establishes that Nginx will respond to requests coming in on port 80.

The location blocks are where we are going to define how our two applications will be served (Angular and ASP.NET). Each time a request comes in, Nginx will look at the URL and try to find the location block that best matches it. In this case the location blocks urls act like a "prefix match", i.e., the first block will match every URL (every url that starts with a /). The second location block matches URLs that start with /api/.

Nginx picks the most "specific" location block, so even though a request for /api/users would match both location blocks, since the second one (/api/) is more specific, it will be the one that would be used to handle the request.

In the first location block (/):

root /pathToYourAngularApplication/dist sets the path where static content will be looked for as the location where your compiled Angular application files are (dist is the CLI's default output folder).

index index.html specifies which file should be served for URLs that end in /.

try_files $uri $uri/ /index.html can be read this way: check if there's a file that matches the normalized URL (e.g. http://www.yourwebsite.com/assets/image.jpg -> /assets/image.jpg), if that file does not exist try the normalized URL plus a / (e.g. http://www.yourwebsite.com/documents -> /documents/ -> /documents/index.html because of the index rule). If all of that fails serve the file /index.html.

Serving /index.html if no match is found is what enables us to use deep linking. For example a URL such as http://www.yourwebsite.com/documentswith no math in the file system will be served with the Angular application's index.html. Index.html will load all the necessary files for the Angular application to run, specifically the routing module. The routing module will then look at the url, and according to the routes defined in the angular app will decide which component to load.

Finally, the last location block. It instructs Nginx to forward the requests that start with /api/ to a webserver that is listening on port 5000 on localhost. That will be your ASP.NET Core's application.

One note about the Nginx's syntax for proxy_pass. It matters a lot if the URL for the application has a / at the end or not. The url in proxy_pass is treated differently if it has what is described in [Nginx's documentation as a "optional URI"](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass "Nginx's documentation as a "optional URI"") (optional URI isn't a great name, since in the end a URL is a URI).

An example of a URL with an optional URI is: http://localhost:5000/optionalURI/. If the location's path is /api/, then a request for http://yourwebsite.com/api/users will be forwarded to your ASP.NET Core's application as http://localhost:5000/optionalURI/users.

That's why not adding the / at the end in proxy_pass is so important, because if you do (e.g.: proxy_pass http://localhost:5000/;) it falls into the "optional URI" category (it will be interpreted as an empty optional URI), and a request for http://yourwebsite.com/api/users will be seen in your ASP.NET Core's application as a request for http://localhost:5000/users.

If you don't add the / at the end (e.g.: proxy_pass http://localhost:5000;) then a request for http://yourwebsite.com/api/users will be seen in the ASP.NET Core application as a request for http://localhost:5000/api/userswhich is probably what you want.

If you need a more complete example that explains how you can make this work outside a development-time scenario (i.e. have your ASP.NET Core application auto start and remain online even if there's an exception) check out HTTPS in ASP.NET Core from Scratch where there's an example describing how you can use Supervisor to keep the ASP.NET application running even in the event of errors (by auto restarting it).

IIS

With IIS it becomes very cumbersome to have a configuration similar to what we can do with Nginx where both the Angular and ASP.NET Core applications are served on port 80.

To understand why it makes it easier if we understand the IIS concepts of Website and Application. When you create a website you define (among other settings) the port (e.g. 80) where it will be served from. A website can then have several applications "inside" it, all of which will share the website configuration (and therefore be served on the same port).

We could for example put our Angular application inside the "Default Web Site" and the ASP.NET Core one as an IIS Application under it, and call it for example "api".

If the "Default Web Site" responds at http://localhost, then the ASP.NET Core application could be at http://localhost/api. Which seems to be exactly what we want. However, the requests for http://localhost/api would be seen in ASP.NET Core without the api in the url.

As far as I know there's no way to change this behavior.

This means your ASP.NET Core application will behave differently when running inside IIS vs when executed directly (either in Visual Studio or with dotnet run).

To make matters worse an ASP.NET Core application needs to be published (dotnet publish) for it to work in IIS. It's not like a non-Core ASP.NET application where you can just point an IIS Application to the folder that contains the ASP.NET application's files .

So when using IIS the reasonable options are to either have ASP.NET Core serve the angular application as it was described in the first section of this article or have two separate Websites.

Lets walk-though the process of creating two separate websites. First a website for the Angular project and then for ASP.NET Core.

Angular in IIS

We'll be adding a Website named MyNgWebSite on port 80. That means that if you have a "Default Web Site", which in all likelihood you'll have, you need to stop it or change its bindings since the default for it is port 80.

But before we get there we need to create an application pool for our Angular application. Right click on Application Pools in IIS Manager:

The Application Pool for an Angular application does not require Managed Code (we only need to serve static files). We should choose "No Managed Code" in the .NET CLR Version:

We can now add a new IIS web site and set the new application pool we created as its application pool:

The physical path should be set to where your Angular project is being compiled to, usually this is the dist folder.

If you were to try to access http://localhost right now (and assuming that you stopped the "Default Web Site" or used a different port than 80) you would get a permissions error. That's because when you create an application pool a ["virtual" user is created](https://docs.microsoft.com/en-us/iis/manage/configuring-security/application-pool-identities ""virtual" user is created"). That user is a local user and must have permissions to access the folder that contains the files you are trying to serve.

That user's name is IIS AppPool\ApplicationPoolName, in this example it's IIS AppPool\ApplicationPoolForAngular.

Go to the folder that contains the compiled Angular project, right click on it and select properties, go to the security tab, click edit, then add and finally add the application pool user:

We should now be able to access your Angular application if you go to http://localhost.

We still need to do one more thing though. Enable deep-linking support.

If you have routes in your Angular application these won't work if someone tries to access them from "outside" the Angular app. What this means is that if navigating to http://localhost/documents is valid inside the Angular application and you send that url to someone else, when that someone else clicks the link they will be greeted with a 404 page from IIS.

That's because there is no documents folder nor index file inside it for IIS to serve. We need to tell IIS that it must serve the file index.html when someone tries to access a URL that does not exists.

We are going to use the same mechanism used for having a custom 404 page, but instead of a 404 page we'll serve the Angular application.

To achieve this we need to create a web.config file and put it in the src folder of the Angular application with this inside:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <httpErrors errorMode="Custom" existingResponse="Replace">
            <remove statusCode="404"/>
            <error statusCode="404" responseMode="ExecuteURL" path="/index.html"/>
        </httpErrors>
    </system.webServer>
</configuration>

A very quick explanation of what's going on. We are using httpErrors with an errorMode="Custom" and existingResponse="Replace". This instructs IIS to replace the default error pages with the one we are about to specify.

remove statusCode="404" will remove any custom settings for 404 pages if they already exist.

error statusCode="404" responseMode="ExecuteURL" path="/index.html" will configure IIS to execute the /index.html url if there's a 404 error. This will effectively serve your Angular application and won't change the URL seen by the client.

Now we need to edit the .angular-cli.json file so that web.config gets copied to the output folder as an asset when the application is compiled. The assets section is under "app", here's an example:

{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
    "name": "your-app"
},
"apps": [
    {
    "root": "src",
    "outDir": "dist",
    "assets": [
        "assets",
        "favicon.ico", 
        "web.config"
    ],
    "index": "index.html",
...

ASP.NET Core in IIS

The process for the configuring an ASP.NET Core application in IIS is similar, although we need to select a different port.

But before you start you need to make sure you have the ASP.NET Core Module for IIS installed. It might already be installed if you installed the .Net Core SDK, however the best way to make sure is to go to IIS Manager and see if it's in the modules' list:

If you don't have it you can find more information about it here and a direct link to download it here.

This module takes care of starting and keeping an ASP.NET Core application running.

Before we create the website in IIS we need the published version of the ASP.NET Core application. You can do that in the command line with dotnet publish or, in full Visual Studio, right click on the project and select Publish, then click publish to folder.

Create a new Website and point it to the ASP.NET Core project published folder, give it a different port number (for example 8080) and create an Application Pool for it.

An application pool for an ASP.NET Core application is also unmanaged (No Managed Code). Although this might seem odd, it's because IIS is actually just acting as a reverse proxy.

Before we're able to run the ASP.NET Project using IIS we need to changed the published folder's permissions so that the Application Pool user can access it. If you don't you'll get this moderately unhelpful error message:

HTTP Error 500.19 - Internal Server Error> HTTP Error 500.19 - Internal Server Error

If you look at the Config Error section you'll see "Cannot read configuration file due to insufficient permissions", which pretty much says it all.

Go to the published folder and add the application pool user to the list of users with permissions over that folder.

Your ASP.NET Core application should now be available on the port you've selected when you created the website in IIS. However, if you try to call it from the Angular application you'll get this error "Failed to load ... No 'Access-Control-Allow-Origin' header is present on the requested resource...". Here's an example of how that would look like in the developer tools console tab:

That's because even though both our our Angular and ASP.NET Core applications are on the same domain, they are in different ports, and that's enough to qualify the request as a Cross Origin Resource Sharing (CORS) request in all browsers except IE.

We need to enable CORS on the ASP.NET Core application. To do that we need to add the package Microsoft.AspNetCore.Cors and in ConfigureServices(IServiceCollection services... method in Startup.csadd services.AddCors():

public void ConfigureServices(IServiceCollection services)
{
    //...
    services.AddCors();
    //...
}

And in the Configure method we need to create a "policy" that says that we are expecting requests from http://localhost. We should do that before the MVC middleware:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    //...
    app.UseCors(builder => builder.WithOrigins("http://localhost"));
    app.UseMvc();
}

You should be good to go. Your Angular and ASP.NET Core should both be working now.

Platform Agnostic Development Setup

Both Angular and ASP.NET Core applications provide ways to detect if they are running in development or production mode. That can be leveraged to create a setup that works both in Linux, Windows or Mac.

The easiest way to run an Angular application is to use run ng serve. That spins up a webpack development server that serves the Angular application on port 4200 by default.

This also has the advantage of having hot module replacing, which means you can see your changes to the Angular application as soon as you make then without even having to refresh the browser.

So ideally we want to run the Angular application this way.

For the ASP.NET Core application we want to run it without having to publish it which you would have to if it is being served by IIS.

This is the ideal development scenario, ng serve for Angular and dotnet run or running the ASP.NET Core from Visual Studio without having to publish it.

In this ideal scenario when developing we could have the Angular application running on port 4200 (through ng serve) and the ASP.NET Core application running on port 5000. When in production the Angular application would typically be served from port 80 and the ASP.NET Core application for port 8080 for example (or from a different server on port 80).

On the ASP.NET Core side of things we'd have to configure CORS to accept requests from port 4200 when in development and from port 80 when in production. In Startup.cs that would look like this:

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors();
    //...        
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    //...
    if (env.IsDevelopment())
    {
        //...
        app.UseCors(builder => builder.WithOrigins("http://localhost:4200"));
    }else 
    {
        app.UseCors(builder => builder.WithOrigins("http://localhost"));
    }

    app.UseMvc();
}

That takes care of the ASP.NET Core application.

For Angular we need to leverage the environemnt.ts and environemnt.prod.ts files. You can find then under a folder name environemnts under the src folder on an Angular project.

What you put on environment.ts will be available when you run in development mode (the default) and the values in environment.prod.ts will be used when in production. To compile the Angular project with the environment set to production use the --env=prod flag (e.g. ng build --env=prod).

Here's a simple example of how the environment files could be configured to support our hypothetical scenario, environment.ts:

export const environment = {
    production: false,
    apiBaseUrl: "http://localhost:4200/"
};

environment.prod.ts:

export const environment = {
    production: true,
    apiBaseUrl: "http://localhost/"
};

In your Angular services, to get to the environment values you just need to import the environment (always environment and not environment.prod):

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

@Injectable()
export class MyServiceService {

    constructor(private httpClient: HttpClient) { }

    getStuff(){
        return this.httpClient.get(`${environment.apiBaseUrl}/api/suff`);
    }  
}

This approach would work even if you host on Nginx or IIS so probably the best option if you need/want to support having developers using different platforms of if you just want to compare performance between them.

How to Create server-side paging with Angular 8 and ASP.NET Core

How to Create server-side paging with Angular 8 and ASP.NET Core

In this post, we will learn how to create server-side paging using Angular 8 and ASP.NET Core, which is very useful whenever we have to display a large number of records.

Introduction

In this post, we will learn how to create server-side paging, which is very useful whenever we have to display a large number of records.

This will come in a total of 3 articles. In this article, we are displaying the number of records. With that, it will calculate the number of pages, but on one page we can see only the selected records, so rather than fetching all of the records, we are going to fetch records based on pages all at once, which will increase our performance.

How will it work?

Suppose that we have 500 records to display in the front end, and we are displaying only 100 records per page. After you click on page 2, it will display the next 100 records and so on. In angular, we can use pipes and install some packages to display pagination, but here if we are only showing 100 records at one time then why we are fetching all 500 records this will decrease our performance.

So, it would be better if we fetch only 100 records at one time and when you click on the next page, it will fetch the next 100 records of that particular page. Here, every time when you click on the page, it will fetch records from the database table.

Prerequisites

  • Basic knowledge of Angular
  • Visual Studio Code must be installed
  • Angular CLI must be installed
  • Node JS must be installed
  • Microsoft Visual Studio 2017 must be installed
  • SQL server 2014.
BACK END

Here Backend related code we will do it using SQL server

The very first step is to create a database

Step 1

Let’s create a database on your local SQL Server. I hope you have installed SQL Server 2017 in your machine (you can use SQL Server 2008, 2012, or 2016, as well).

create database company  

Step 2

Create CompanyDetails Table using the following code

CREATE TABLE [dbo].[CompanyDetails](  
    [CompanyId] [int] IDENTITY(1,1) NOT NULL,  
    [CompanyName] [nvarchar](100) NULL,  
    [City] [nvarchar](50) NULL,  
    [State] [nvarchar](50) NULL,  
    [Owner] [nvarchar](50) NULL,  
    [PublishYear] [int] NULL,  
 CONSTRAINT [PK_CompanyDetails] PRIMARY KEY CLUSTERED   
(  
    [CompanyId] ASC  
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]  
) ON [PRIMARY]  
GO  

Now let's add Store Procedures

Step 3

Create Stored Procedure of the following:

**GetAllCompanies **

Create Proc [dbo].[Usp_GetAllCompanies]  
 @PageNo INT ,  
 @PageSize INT ,  
 @SortOrder VARCHAR(200)  
As  
Begin  
  
    Select * From   (Select ROW_NUMBER() Over (  
    Order by CompanyName ) AS 'RowNum', *  
         from   [CompanyDetails]  
        )t  where t.RowNum Between ((@PageNo-1)*@PageSize +1) AND (@PageNo*@pageSize)  
        
End   

GetAllCompaniesCount

Create Proc [dbo].[Usp_getAllCompaniesCount]  
As  
  
Begin  
        select count(CompanyId) from   [CompanyDetails]  
End  
WEB API

Create an ASP.NET Core application

Follow these steps to create an ASP.NET Core application.

Step 1

In Visual Studio 2019, click on File -> New -> Project.

Step 2

Choose the Create option and select the ASP.NET web application.

Step 3

Select Web API and click Ok.

Step 4

Now right click on the controller and add a new item.

Step 5

Choose Ado.net Entity Data Model and then click on Add

Step 6

Next Step is EF Designer, just click on next.

Step 7

A new pop-up will show. Click on next. If your's isn't established, then click on new connection

Step 8

Copy your database connection server name and paste it in the server name textbox. You will see all the database, select your database and click on ok.

Step 9

The next popup will show, paste your database server name, and choose for the database and test for the connection then click on next. Here, in the new screen, select your tables and store the procedure. Then click on finish.

Our next step is to right-click on the Controllers folder and add a new controller. Name it as "Paginationcontroller" and add the following namespace in the Paginationcontroller.

Here is the complete code for getting all the Pagination records.

Complete Pagination controller code

using System.Collections.Generic;  
using System.Data.Entity.Core.Objects;  
using System.Linq;  
using System.Web.Http;  
using Pagination.Models;  
namespace Pagination.Controllers  
{  
    public class PaginationController : ApiController  
    {  
        CompanyEntities2 db = new CompanyEntities2();  
  
        [HttpGet]  
        public object getAllCompanies(int pageNo, int pageSize, string sortOrder)  
        {  
  
            var oMyString = new ObjectParameter("totalCount", typeof(int));  
  
            var companyDetails = db.Usp_GetAllCompanies(pageNo, pageSize, sortOrder).ToList();  
            return companyDetails;  
        }  
  
        [HttpGet]  
        public object getAllCompaniesCount()  
        {  
  
            var companyDetailsCount = db.Usp_getAllCompaniesCount().SingleOrDefault();         
            return companyDetailsCount;  
        }  
    }  
}  
FRONT END

Step 1

Let's create an angular project using following npm command

ng new pagination

Step 2

Open the newly created project in visual studio code and install bootstrap in this project

npm install bootstrap --save   

Now open styles.css file and add Bootstrap file reference.To add reference in styles.css file add this line.

@import '~bootstrap/dist/css/bootstrap.min.css'; 

Step 3

Now let's create a new component by using the following command.

ng g c pagination  

Step 4

Now create a new service using the following command.

ng generate service pagination   

Step 5

Now open the pagination.component.html and paste the following code to see the HTML template.

<div class="row">    
  <div class="col-12 col-md-12">    
    <div class="card">    
      <div class="card-header">    
        Companies 1-{{pageSize}} (Total:{{totalCompaniesCount}})    
      </div>    
      <div class="card-body position-relative">    
           
        <div class="table-responsive cnstr-record companie-tbl">    
          <table class="table table-bordered heading-hvr">    
            <thead>    
              <tr>    
                <th style="cursor: pointer;" [ngClass]="order =='CompanyNumber'? 'active':''"    
                  (click)="setOrder('CompanyNumber')" width="80">Company Name.</th>    
                <th style="cursor: pointer;" [ngClass]="order =='CompanyType'? 'active':''"    
                  (click)="setOrder('CompanyType')" width="75">City</th>    
                <th [ngClass]="order =='CompanyName'? 'active':''" style="cursor: pointer;"    
                  (click)="setOrder('CompanyName')">State    
                </th>    
                <th [ngClass]="order =='OrgNo'? 'active':''" style="cursor: pointer;" (click)="setOrder('OrgNo')"    
                  width="75">Owner    
                </th>    
                <th [ngClass]="order =='Street'? 'active':''" style="cursor: pointer; width:250px"    
                  (click)="setOrder('Street')">Publish Year</th>    
              </tr>    
            </thead>    
            <tbody>    
              <tr *ngFor="let item of companies">    
                <td>{{item.CompanyName}}</td>    
                <td>{{item.City}}</td>    
                <td>{{item.State}}</td>    
                <td>{{item.Owner}}</td>    
                <td>{{item.PublishYear}}</td>    
    
              </tr>    
            </tbody>    
          </table>    
    
              
        </div>    
        <!-- Code by pagination -->    
        <div class="container mw-100">    
          <div class="row">    
            <div class="col-md-3"> </div>    
            <div *ngIf="companies !=0" class="col-md-6">    
              <ul class="pagination justify-content-center">    
                <li *ngFor="let page of pageField;let i=index" class="page-item">    
                  <a (click)="showCompaniesByPageNumber(page,i)" [ngClass]="pageNumber[i] ? 'pageColor':'page-link'"    
                    style=" margin-right: 5px;;margin-top: 5px">{{page}}</a>    
                       
                </li>    
              </ul>    
              <div style="text-align: center;">    
                Page {{currentPage}} of Total page {{paginationService.exactPageList}}    
              </div>    
            </div>    
          </div>    
        </div>    
      </div>    
    </div>    
  </div>    
</div>     

Step 6

Open the pagination.component.ts file and add the following code in this file where our logic has been written.

import { Component, OnInit } from '@angular/core';  
import { ApiService } from './api.service';  
import { PaginationService } from './pagination.service';  
  
@Component({  
  selector: 'app-pagination',  
  templateUrl: './pagination.component.html',  
  styleUrls: ['./pagination.component.css']  
})  
export class PaginationComponent implements OnInit {  
  companies = [];  
  pageNo: any = 1;  
  pageNumber: boolean[] = [];  
  sortOrder: any = 'CompanyName';  
  //Pagination Variables  
  
  pageField = [];  
  exactPageList: any;  
  paginationData: number;  
  companiesPerPage: any = 5;  
  totalCompanies: any;  
  totalCompaniesCount: any;  
  
  constructor(public service: ApiService, public paginationService: PaginationService) { }  
  
  ngOnInit() {  
    this.pageNumber[0] = true;  
    this.paginationService.temppage = 0;  
    this.getAllCompanies();  
  }  
  getAllCompanies() {  
    this.service.getAllCompanies(this.pageNo, this.companiesPerPage, this.sortOrder).subscribe((data: any) => {  
      this.companies = data;  
      this.getAllCompaniesCount();  
    })  
  }  
  
  //Method For Pagination  
  totalNoOfPages() {  
  
    this.paginationData = Number(this.totalCompaniesCount / this.companiesPerPage);  
    let tempPageData = this.paginationData.toFixed();  
    if (Number(tempPageData) < this.paginationData) {  
      this.exactPageList = Number(tempPageData) + 1;  
      this.paginationService.exactPageList = this.exactPageList;  
    } else {  
      this.exactPageList = Number(tempPageData);  
      this.paginationService.exactPageList = this.exactPageList  
    }  
    this.paginationService.pageOnLoad();  
    this.pageField = this.paginationService.pageField;  
  
  }  
  showCompaniesByPageNumber(page, i) {  
    this.companies = [];  
    this.pageNumber = [];  
    this.pageNumber[i] = true;  
    this.pageNo = page;  
    this.getAllCompanies();  
  }  
  
  getAllCompaniesCount() {  
    this.service.getAllCompaniesCount().subscribe((res: any) => {  
      this.totalCompaniesCount = res;  
      this.totalNoOfPages();  
    })  
  }  
  
}  

Step 7

Next open pagination.component.css file and paste the code for some styling.

@charset "utf-8";     
/* CSS Document */    
@media all{    
    *{padding:0px;margin:0px;}    
div{vertical-align:top;}    
img{max-width:100%;}    
html {-webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale;}    
body{overflow:auto!important; width:100%!important;}    
html, body{background-color:#e4e5e6;}    
html {position:relative; min-height:100%;}    
    
    
.card{border-radius:4px;}    
.card-header:first-child {border-radius:4px 4px 0px 0px;}    
    
/*Typekit*/    
html, body{font-family:'Roboto', sans-serif; font-weight:400; font-size:13px;}    
body{padding-top:52px;}    
    
p{font-family:'Roboto', sans-serif; color:#303030; font-weight:400; margin-bottom:1rem;}    
input, textarea, select{font-family:'Roboto', sans-serif;}    
    
h1,h2,h3,h4,h5,h6{font-family:'Roboto', sans-serif; font-weight:700;}    
h1{font-size:20px; color:#000000; margin-bottom:10px;}    
h2{font-size:30px;}    
h3{font-size:24px;}    
h4{font-size:18px;}    
h5{font-size:14px;}    
h6{font-size:12px;}    
    
.row {margin-right:-8px; margin-left:-8px;}    
.col, .col-1, .col-10, .col-11, .col-12, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-auto, .col-lg, .col-lg-1, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-auto, .col-md, .col-md-1, .col-md-10, .col-md-11, .col-md-12, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-auto, .col-sm, .col-sm-1, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-auto, .col-xl, .col-xl-1, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-auto {padding-right:8px; padding-left:8px;}    
    
.card-header{background-color:#f0f3f5; border-bottom:1px solid #c8ced3; font-size:13px; font-weight:600; color:#464646; text-transform:uppercase; padding:.75rem 8px;}    
    
    
.cnstr-record th{white-space:nowrap;padding:.45rem .2rem; font-size:13px; border-bottom-width:0px!important;}    
.cnstr-record thead{background:#f0f3f5;}    
    
.cnstr-record .form-control{font-size:13px; padding:0px 0rem 0px 0.2rem; height:calc(2rem + 2px);}    
.cnstr-record select.form-control{padding-left:.05rem;}    
.cnstr-record .table td, .cnstr-record .table th {vertical-align:middle;}    
.cnstr-record .table td{padding:.3rem;}    
.cnstr-record .table td h4{margin:0px;}    
    
.wp-50{width:50px;}    
.wp-60{width:60px;}    
.wp-70{width:70px;}    
.wp-80{width:80px;}    
.wp-90{width:90px;}    
.wp-100{width:100px;}    
.mw-auto{min-width:inherit;}    
.expand-row{width:100%; border:solid 1px #596269; display:inline-block; border-radius:3px; width:16px; height:16px; vertical-align:top; background:#596269; color:#ffffff!important;}    
.expand-row img{vertical-align:top; position:relative; top:2px;}    
.sub-table th{font-weight:400; font-size:12px;}    
.sub-table td{background:#efefef;}    
.no-bg td{background:inherit;}    
.mw-100{max-width:100%;}    
    
  
  
.activeTabColor{  
    color: #fff;  
    background-color: #000000;  
}   
.page-item:first-child .page-link {  
    margin-left: 0;  
    border-top-left-radius: .25rem;  
    border-bottom-left-radius: .25rem;  
}  
  
.pageColor{  
    position: relative;  
    display: block;  
    padding: .5rem .75rem;  
    margin-left: -1px;  
    line-height: 1.25;  
    color: white!important;  
    background-color: black!important;  
    border: 1px solid #dee2e6;  
}  
.notAllowed{  
    position: relative;  
    display: block;  
    padding: .5rem .75rem;  
    margin-left: -1px;  
    line-height: 1.25;  
    color: #007bff;  
    background-color: #fff;  
    border: 1px solid #dee2e6;  
    cursor: not-allowed;  
}  
.page-link {  
    position: relative;  
    display: block;  
    padding: .5rem .75rem;  
    margin-left: -1px;  
    line-height: 1.25;  
    color: #007bff;  
    background-color: #fff;  
    border: 1px solid #dee2e6;  
}  
  
}     

Step 8

At last, open pagination.service.ts file and add services to call our API.

import { Injectable } from '@angular/core';  
  
@Injectable()  
  
export class PaginationService {  
    //Pagination Variables  
  
    temppage: number = 0;  
    pageField = [];  
    exactPageList: any;  
  
    constructor() {  
    }  
  
    // On page load   
    pageOnLoad() {  
        if (this.temppage == 0) {  
  
            this.pageField = [];  
            for (var a = 0; a < this.exactPageList; a++) {  
                this.pageField[a] = this.temppage + 1;  
                this.temppage = this.temppage + 1;  
            }  
        }  
    }  
  
} 

Step 9

Let's add the following code in api.service.ts file:

import { Injectable } from '@angular/core';  
import { HttpClient } from '@angular/common/http';  
import { Observable } from 'rxjs';  
  
@Injectable({  
  providedIn: 'root'  
})  
export class ApiService {  
    private url = "";  
  
    constructor(public http: HttpClient) {  
    }    
  
getAllCompanies(pageNo,pageSize,sortOrder): Observable<any> {  
    this.url = 'http://localhost:59390/api/Pagination/getAllCompanies?pageNo=' + pageNo+'&pageSize='+pageSize+'&sortOrder='+sortOrder;  
    return this.http.get(this.url);  
  }  
  
  getAllCompaniesCount(): Observable<any> {  
    this.url = 'http://localhost:59390/api/Pagination/getAllCompaniesCount';  
    return this.http.get(this.url);  
  }  
}  

Step 10

Next and last step is to add the app module file in your project.

import { BrowserModule } from '@angular/platform-browser';  
import { NgModule } from '@angular/core';  
  
import { AppComponent } from './app.component';  
import { ApiService } from './pagination/api.service';  
import { HttpClientModule } from '@angular/common/http';  
import { PaginationService } from './pagination/pagination.service';  
import { PaginationComponent } from './pagination/pagination.component';  
  
@NgModule({  
  declarations: [  
    AppComponent,  
    PaginationComponent  
  ],  
  imports: [  
    BrowserModule,  
    HttpClientModule  
  ],  
  providers: [ApiService,PaginationService],  
  bootstrap: [AppComponent]  
})  
export class AppModule { }  

Step 10

Now, its time to see the output. For that, just open your terminal and type "ng serve -o" to compile and open automatically in your browser.

After loading our page you can see the output like in the below images.

Here, the total number of records is 33 based on that our logic in frontend will calculate the number of pages i.e at one page we are displaying only 5 records(can be change) so until the 6th page, there are 5 records per page which means only 3 records are remaining now which will come in the last page, which is 7th one.

Page 2: The total number of records to display is 5.

On the last page, only 3 records will show.

With this step, we have successfully completed our frontend, web API and backend coding.

Conclusion

In this article, I tried to explain how you get the records and display it in paging using server-side pagination using angular 8 and ASP.NET.

Thank you for reading and keep visitting!

Build a Basic Website with ASP.NET MVC and Angular

Build a Basic Website with ASP.NET MVC and Angular

Build a Basic Website with ASP.NET MVC and Angular. Get Started with ASP.NET MVC. Create an Angular Project. Set up a Database for ASP.NET. Add Authentication to Your Angular Application. Set Up Your ASP.NET API Endpoints. Set a Default Formatter for ASP.NET Web API 2.

Build a Basic Website with ASP.NET MVC and Angular. Get Started with ASP.NET MVC. Create an Angular Project. Set up a Database for ASP.NET. Add Authentication to Your Angular Application. Set Up Your ASP.NET API Endpoints. Set a Default Formatter for ASP.NET Web API 2.

ASP.NET has been around for a long time. When Microsoft introduced ASP.NET MVC, it changed the way many developers approach their codebase. There is an excellent separation of concerns, a TDD friendly framework, and easy integration with JavaScript while maintaining full control over rendered HTML. On the client side, a lot of .NET developers prefer Angular because it comes with TypeScript and it’s a much closer language to C# than plain JavaScript. Angular is an excellent framework for building enterprise-level, feature rich, applications.

You will be using .NET Framework 4.7.1, and you should have the latest version of Visual Studio 2017 installed. Also, you should have Node and npm installed since you will use Angular tools that require Node and npm.

Get Started with ASP.NET MVC

Create an API using a built-in template within Visual Studio. You’ll start from a scratch.

In Visual Studio, select File -> New Project

Select Web, ASP.NET Web Application (.NET Framework)

On the following screen select MVC:

You should have a new ASP.NET MVC application ready now, with all the folders and files as shown in the following picture:

At this moment, in your project, you have some NuGet packages you won’t be using. You can get rid of them quite quickly. Run the following commands inside of your Package Manager Console

Uninstall-Package Microsoft.ApplicationInsights.Web
Uninstall-Package Microsoft.ApplicationInsights.WindowsServer
Uninstall-Package Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel
Uninstall-Package Microsoft.ApplicationInsights.PerfCounterCollector
Uninstall-Package Microsoft.ApplicationInsights.DependencyCollector
Uninstall-Package Microsoft.ApplicationInsights.Agent.Intercept
Uninstall-Package Microsoft.ApplicationInsights


Additionally, make sure you delete ApplicationInsights.config file.

After finishing, you should have a clean project and perfect starting ground.

Create an Angular Project

Using Angular CLI is the best way to get started with Angular applications. With Angular CLI tools, you can easily create new projects, update existing projects, add different components to your codebase, and do all of that by entering a few commands in your terminal. You can also run the application in the development environment and publish it to production environments. It saves you the trouble of setting up configuration and Webpack. It can also do all the optimizing, compressing and minifying for you. It’s highly recommended to use Angular CLI to prepare your applications for production environments.

You can install the Angular CLI by entering the following in your favorite terminal:

npm install -g @angular/[email protected]


After that, navigate to the folder of your MVC project and open your terminal there and enter the following:

ng new Angular --skip-tests --style=scss


This will create a new Angular project under Angular folder within your MVC project. The --skip-tests argument will make sure that CLI skips creating spec files.

You should include the Angular folder in your MVC project. To do that, click on the Show All Files button in your Solution Explorer. You can now navigate to Angular folder and include all folders and files except for node_modules. After you do that you should have a folder structure like on the following picture:

Set up a Database for ASP.NET

You will use Entity Framework (EF) 6 as your Object-Relational Mapper (ORM), a proven solution from Microsoft. EF 6 is pretty easy to set up and get going, and you don’t need to know any SQL to have it working for your applications. Think of it as an adapter or bridge to your database. All of your database queries will go through Entity Framework.

The central point is your DbContext class, and that’s where you define your connection string and the tables for your database. EF 6 uses the DbSet collection type to represent a table in a database. All you need to do is create a connection string, create a new class that inherits from DbContext, make the data models and create appropriate properties within DbContext for those models. Those properties will represent tables in the database. From that point, it’s pretty easy to get and update existing data by using Language Integrated Queries (LINQ).

You should first install the EF 6, by running the following code in the Package Manager Console:

Install-Package EntityFramework -Version 6.2.0


Add the Connection String

You can get started by adding a connection string to your Web.config file. Make sure to add it inside of the section, and after :

<connectionStrings>
  <add name="OktaConnectionString"
    connectionString="Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=JoggingTracker;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\WeightTracker.mdf"
    providerName="System.Data.SqlClient"
  />
</connectionStrings>


Create Your DbContext and Data Models in Angular

First, create a model for a recording a jog. Create a file in the Models directory called JoggingRecord.cs:

using System;

namespace OktaAspNetMvcAngular.Models
{
  public class JoggingRecord
  {
    public int Id { get; set; }

    public float Distance { get; set; }

    public string Description { get; set; }

    public DateTime CreatedAt { get; set; }
  }
}


Nothing special here, a standard Id property that represents a unique identifier for every row within a table and Entity Framework will use a naming convention and automatically set it as a primary key for this table. You definitely need the Distance property to track your jogs, a Description to describe the jog and CreatedAt property for an audit trail.

Next, create an ApplicationDbContext class inside of a new folder called Data. For now, it will be pretty straightforward:

using System.Data.Entity;
using OktaAspNetMvcAngular.Models;

namespace OktaAspNetMvcAngular.Data
{
  public class ApplicationDbContext : DbContext
  {
    public ApplicationDbContext() : base("OktaConnectionString")
    {
    }

    public static ApplicationDbContext Create() => new ApplicationDbContext();

    public DbSet<JoggingRecord> JoggingRecords { get; set; }
  }
}


The piece of code that calls the base DbContext class is used to set up the connection string. There is the DbSet property that I previously mentioned, and it will be used to fetch records from the table, update and delete those records.

Enable and Run Migrations in Your ASP.NET Project

Open the Package Manager Console, and run the following to enable migrations:

Enable-Migrations


Now you can add your initial migration, which will contain the creation of the JoggingRecords table:

Add-Migration Initial


Now, you can update the database:

Update-Database -Verbose


The verbose flag will allow you to see the generated SQL statements, and this can help you in resolving errors if any occur.

Seed the Database

After you have updated the database, add Migrations by adding code inside of the Seed method in the Configuration class.

First, add the using for the Models namespace:

using OktaAspNetMvcAngular.Models;


After that, add the following code for the Seed method:

protected override void Seed(OktaAspNetMvcAngular.Data.ApplicationDbContext context)
{
  context.JoggingRecords.AddOrUpdate(x => x.Id,
    new JoggingRecord { Id = 1, Description = "Friday Evening", Distance = 5.5f, CreatedAt = new DateTime(2018, 5, 1) },
    new JoggingRecord { Id = 2, Description = "Saturday morning", Distance = 6.15f, CreatedAt = new DateTime(2018, 4, 4) },
    new JoggingRecord { Id = 3, Description = "Marathon", Distance = 20, CreatedAt = new DateTime(2018, 5, 13) },
    new JoggingRecord { Id = 4, Description = "Short one", Distance = 3.5f, CreatedAt = new DateTime(2018, 6, 6) }
  );

  //  This method will be called after migrating to the latest version.

  //  You can use the DbSet<T>.AddOrUpdate() helper extension method
  //  to avoid creating duplicate seed data.
}


Add Authentication to Your Angular Application

Handling the authentication is never an easy or comfortable task. If you want to quickly and easily take care of authentication for your application you’re likely in the market for a solution like Okta. That’s why so many developers love and use Okta, they never want to build auth again and are happy to leave it to us.

To get started with Okta, sign up for a forever-free developer account or (log in if you already have one).

You should see the Dashboard and in the upper right corner, there should be your unique Org URL. Save it for later.

Now you need to create a new application by browsing to the Applications tab and clicking Add Application, and from the first page of the wizard choose Single-Page App.

On the settings page, enter OktaMvcAngular as your name value and select Implicit(Hybrid).

Now that your application has been created copy down the Client ID and Client secret values on the following page, you’ll need them soon (of course, yours will be different).

It is quite easy to add authentication to ASP.NET MVC with help of Okta.

Add the following to your Web.config file, inside of :

<!-- 1\. Replace these values with your Okta configuration -->
<add key="okta:ClientId" value="{yourClientId}" />
<add key="okta:ClientSecret" value="{yourClientSecret}" />
<add key="okta:OrgUri" value="https://{yourOktaDomain}/oauth2/default" />

<!-- 2\. Update the Okta application with these values -->
<add key="okta:RedirectUri" value="http://localhost:8080/authorization-code/callback" />
<add key="okta:PostLogoutRedirectUri" value="http://localhost:8080/Account/PostLogout" />


Install the required NuGet packages:

Install-Package Okta.AspNet -Version 1.1.1 
Install-Package Microsoft.Owin.Host.SystemWeb -Version 4.0.0
Install-Package Microsoft.Owin.Security.Cookies -Version 4.0.0


Add the Startup class. Right click on the project -> Add new item -> OWIN Startup Class:

Replace the content of that file with the following code:

using IdentityModel.Client;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using Owin;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Security.Claims;
using System.Threading.Tasks;

[assembly: OwinStartup(typeof(OktaAspNetMvcAngular.Startup))]

namespace OktaAspNetMvcAngular
{
  public class Startup
  {
    // These values are stored in Web.config. Make sure you update them!
    private readonly string clientId = ConfigurationManager.AppSettings["okta:ClientId"];
    private readonly string redirectUri = ConfigurationManager.AppSettings["okta:RedirectUri"];
    private readonly string authority = ConfigurationManager.AppSettings["okta:OrgUri"];
    private readonly string clientSecret = ConfigurationManager.AppSettings["okta:ClientSecret"];
    private readonly string postLogoutRedirectUri = ConfigurationManager.AppSettings["okta:PostLogoutRedirectUri"];

    public void Configuration(IAppBuilder app)
    {
      // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=316888
      app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
      app.UseCookieAuthentication(new CookieAuthenticationOptions());
      app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
      {
        ClientId = clientId,
        ClientSecret = clientSecret,
        Authority = authority,
        RedirectUri = redirectUri,
        ResponseType = OpenIdConnectResponseType.CodeIdToken,
        Scope = OpenIdConnectScope.OpenIdProfile,
        PostLogoutRedirectUri = postLogoutRedirectUri,
        TokenValidationParameters = new TokenValidationParameters
        {
          NameClaimType = "name"
        },

        Notifications = new OpenIdConnectAuthenticationNotifications
        {
          AuthorizationCodeReceived = async n =>
          {
            // Exchange code for access and ID tokens
            var tokenClient = new TokenClient(authority + "/v1/token", clientId, clientSecret);
            var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(n.Code, redirectUri);

            if (tokenResponse.IsError)
            {
              throw new Exception(tokenResponse.Error);
            }

            var userInfoClient = new UserInfoClient(authority + "/v1/userinfo");
            var userInfoResponse = await userInfoClient.GetAsync(tokenResponse.AccessToken);
            var claims = new List<Claim>();
            claims.AddRange(userInfoResponse.Claims);
            claims.Add(new Claim("id_token", tokenResponse.IdentityToken));
            claims.Add(new Claim("access_token", tokenResponse.AccessToken));

            if (!string.IsNullOrEmpty(tokenResponse.RefreshToken))
            {
              claims.Add(new Claim("refresh_token", tokenResponse.RefreshToken));
            }

            n.AuthenticationTicket.Identity.AddClaims(claims);

            return;
          },
          RedirectToIdentityProvider = n =>
          {
            // If signing out, add the id_token_hint
            if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
            {
              var idTokenClaim = n.OwinContext.Authentication.User.FindFirst("id_token");
              if (idTokenClaim != null)
              {
                n.ProtocolMessage.IdTokenHint = idTokenClaim.Value;
              }
            }

            return Task.CompletedTask;
          }
        },
      });
    }
  }
}


Add an a new MVC 5 empty controller inside of Controllers folder. You can name it AccountController and paste the following code inside of it:

using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using System.Web;
using System.Web.Mvc;

namespace OktaAspNetMvcAngular.Controllers
{
  public class AccountController : Controller
  {
    public ActionResult Login()
    {
      if (!HttpContext.User.Identity.IsAuthenticated)
      {
        HttpContext.GetOwinContext().Authentication.Challenge(OpenIdConnectAuthenticationDefaults.AuthenticationType);
        return new HttpUnauthorizedResult();
      }

      return RedirectToAction("Index", "Home");
    }

    [HttpPost]
    public ActionResult Logout()
    {
      if (HttpContext.User.Identity.IsAuthenticated)
      {
        HttpContext.GetOwinContext().Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType, OpenIdConnectAuthenticationDefaults.AuthenticationType);
      }

      return RedirectToAction("Index", "Home");
    }

    public ActionResult PostLogout()
    {
      return RedirectToAction("Index", "Home");
    }
  }
}


Add Login Section to Layout

Inside of Views/Shared folder create a new partial view named _LoginPartial and paste the following code inside:

@if (Context.User.Identity.IsAuthenticated)
{
  <ul class="nav navbar-nav navbar-right">
    <li>
      <p class="navbar-text">Hello, <b>@Context.User.Identity.Name</b></p>
    </li>
    <li>
      <a onclick="document.getElementById('logout_form').submit();" style="cursor: pointer;">Log out</a>
    </li>
  </ul>
  <form action="/Account/Logout" method="post" id="logout_form"></form>
}
else
{
  <ul class="nav navbar-nav navbar-right">
    <li>@Html.ActionLink("Log in", "Login", "Account")</li>
  </ul>
}


Set Up Your ASP.NET API Endpoints

You will use the controller as your endpoint source for the API. Add a new Web API controller namedJoggingRecordsController.cs class inside of your Controllers folder and paste the following code:

using OktaAspNetMvcAngular.Data;
using OktaAspNetMvcAngular.Models;

using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Description;

namespace OktaAspNetMvcAngular.Controllers
{
  public class JoggingRecordsController : ApiController
  {
    private ApplicationDbContext db = new ApplicationDbContext();

    // GET: api/JoggingRecords
    public IQueryable<JoggingRecord> GetJoggingRecords() => db.JoggingRecords;

    // GET: api/JoggingRecords/5
    [ResponseType(typeof(JoggingRecord))]
    public async Task<IHttpActionResult> GetJoggingRecord(int id)
    {
      JoggingRecord joggingRecord = await db.JoggingRecords.FindAsync(id);
      if (joggingRecord == null)
      {
        return NotFound();
      }

      return Ok(joggingRecord);
    }
  }
}


The code above adds two endpoints. The first one will fetch all JoggingRecords from the database. In your applications, you will probably want some kind of paging and filtering. The second endpoint will fetch individual JoggingRecords instances based on the id parameter that the client will pass to the API. That parameter will match the Id in JoggingRecords table in the database.

Set a Default Formatter for ASP.NET Web API 2

Unfortunately, ASP.NET Web API 2 is set to use XML as default formatter. Remove it and make sure JSON formatter is the default one. Add the following code at the end of the Register() method inside of WebApiConfig.cs file:

// Set JSON formatter as default one and remove XmlFormatter

var jsonFormatter = config.Formatters.JsonFormatter;
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
config.Formatters.Remove(config.Formatters.XmlFormatter);
jsonFormatter.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;


You’ll also need to add the following using statement for the JSON serializer to the file:

using Newtonsoft.Json.Serialization;


You will also want to specify the port for your ASP.NET application. You need to know the exact address so you can communicate with the API from your Angular application. To do so, right click on the project in the solution explorer and click properties. In the main properties window, choose Web from the left-hand menu and set the Project Url property to http://localhost:8080.

Set Up the Angular Application for ASP.NET MVC

Since you will let your ASP.NET MVC application handle routing you can remove the app-routing.module.ts file and its references in the AppModule. Your app.module.ts file should look like this:

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

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

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


After that is done, delete the code from app.component.html.

You also need to make sure that the Angular CLI outputs files to the appropriate folder. Inside of your Angular folder open angular.json file and change the outputPath setting:

"outputPath": "../Bundles/AngularOutput",


In the ASP.NET MVC application, you will add a new JS and CSS bundle for Angular. Locate the BundleConfig.cs file inside of App_Start folder and at the end of the RegisterBundles function add the following code:

// Angular bundles
bundles.Add(new ScriptBundle("~/bundles/Angular")
  .Include(
    "~/bundles/AngularOutput/inline.*",
    "~/bundles/AngularOutput/polyfills.*",
    "~/bundles/AngularOutput/scripts.*",
    "~/bundles/AngularOutput/vendor.*",
    "~/bundles/AngularOutput/runtime.*",
    "~/bundles/AngularOutput/main.*"));

bundles.Add(new StyleBundle("~/Content/Angular")
  .Include("~/bundles/AngularOutput/styles.*"));


For this to work, you need to make use of these bundles inside of your views. You should first create a new MVC view for your Angular application. Create a new folder inside of Views folder called AngularData and inside of it create new Index.cshtml file:

@{
  ViewBag.Title = "Index";
}

@Styles.Render("~/Content/Angular")

<app-root>test</app-root>

@Scripts.Render("~/bundles/Angular")


The code above will actually render the JS bundle that our angular application produces. Angular will find the tag and render the App component and everything inside of it.

To access this view, you need to create a matching controller. Inside of Controllers folder create a new MVC 5 controller named AngularDataController:

using System.Web.Mvc;

namespace OktaAspNetMvcAngular.Controllers
{
  public class AngularDataController : Controller
  {
    public ActionResult Index()
    {
      return View();
    }
  }
}


You should also update the layout file so that you can access the Angular application from the menu bar. Locate the _Layout.cshtml file inside of Views/Shared folder. Make sure your navbar looks like this:

<ul class="nav navbar-nav">
  <li>@Html.ActionLink("Home", "Index", "Home")</li>
  <li>@Html.ActionLink("Angular", "Index", "AngularData")</li>
  <li>@Html.ActionLink("About", "About", "Home")</li>
  <li>@`Html.ActionLink("Contact", "Contact", "Home")</li>
</ul>


To have the Angular application outputting the content, navigate inside of the Angular folder and open your favorite terminal. Enter the following command:

ng build --extractCss --watch


The command above will make sure that new bundle is produced whenever you change any code that is part of the Angular codebase, and it will also extract CSS from global styles into CSS files instead of JS ones.

Create a Model and API Service for Your Angular Application

Your app contains only one root module, AppModule, and one component AppComponent. Since the application that you will be making here is a small one you will not create additional modules. Once the application grows and gets more complicated, you should introduce new modules, shared modules to organize your code better. The application itself will only show some records from the API. However, since your Angular application is interacting with the server, you should create a separate Angular service for this purpose.

You can also create a TypeScript model for the JoggingRecord. First create a shared folder inside the app folder, which is part of the Angular application that gets created by the Angular CLI.

Inside of the shared folder create a new file — JoggingRecord.ts:

export default class JoggingRecord {
  id: number;
  distance: number;
  description: string;
  createdAt: string;
}


The class above will allow you to have a strongly typed model for the JoggingRecord inside of your Angular application.

You can now create the API service inside of the shared folder. Name the file api.service.ts:

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

import JoggingRecord from './JoggingRecord';

@Injectable()
export default class ApiService {
  public API = 'http://localhost:8080/api';
  public JOGGING_RECORDS_ENDPOINT = `${this.API}/joggingrecords`;

  constructor(private http: HttpClient) { }

  getAll(): Observable<Array<JoggingRecord>> {
    return this.http.get<Array<JoggingRecord>>(this.JOGGING_RECORDS_ENDPOINT);
  }
}


The ApiService class is the bridge between our Angular application and our Web API endpoints. You need it to fetch the data from our server.

Add the service to the App module by updating the providers array inside of app.module.ts file. You also need to import the HttpClientModule. Your app.module.ts file should end up looking like this:

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

import { AppComponent } from './app.component';
import ApiService from '../shared/api.service';

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


Fetch Data from the ASP.NET API Endpoint

Update the app.component.ts file with the following content:

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

import JoggingRecord from '../shared/JoggingRecord';
import ApiService from '../shared/api.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit  {
  joggingRecords: Array<JoggingRecord>;

  constructor(private apiService: ApiService) {
  }

  ngOnInit() {
    this.apiService.getAll().subscribe(data => {
      this.joggingRecords = data;
    });
  }
}


The code above will subscribe will assign the data that ApiService fetches from server to the joggingRecords property.

You also need to update this component’s template (app.component.html):

<div style="text-align:center">
  <h1>
    Jogging Records
  </h1>

  <span *ngFor="let record of joggingRecords">
    {{record.description}} - {{record.distance}}
    <br />
  </span>
</div>


Test Out Your ASP.NET and Angular Application

Since you already have the Angular CLI outputting the code changes for your Angular application in the Bundles/AngularOutput folder, and your MVC application is making use of that folder you can simply start the MVC application and preview Angular app inside of it.

From your Visual Studio use CTRL+F5 to start the application your browser should open and you should see the following screen:

Clicking on the Angular link in the menu will actually render the MVC view for the Angular application and from that point it’s Angular who is in charge of rendering. The Angular page should look like this:

If you open the Dev Tools on your editor you should see that the Angular application actually hits the ASP.NET API:

Learn More

Angular 7 (formerly Angular 2) - The Complete Guide

Learn and Understand AngularJS

Angular Crash Course for Busy Developers

The Complete Angular Course: Beginner to Advanced

Angular (Angular 2+) & NodeJS - The MEAN Stack Guide

Become a JavaScript developer - Learn (React, Node,Angular)

Angular (Full App) with Angular Material, Angularfire & NgRx

The Web Developer Bootcamp

The Complete ASP.NET MVC 5 Course

Build a Real-world App with ASP.NET Core and Angular 2 (4+)

ASP NET Core (ASP.NET 5),MVC 6,C#,Angular2 & EF Crash Course