How to visualize live data using SignalR and Angular
Tags: signalr, angular, webdev
Published: 2020-04-27 08:51:23

In this blog, we will take a look at a simple scenario to use SignalR to track live data and feed it in our client application. For this let us consider a simple scenario where we need to know what's the price of a BitCoin in various countries right now or get stock market prices. You can also think of any other scenario where the data gets changed every second.

Let's look at what you need to have and what you need to know before we get into detail.

### Prerequisites

1. A computer 💻 😝
2. Visual Studio or VS Code 🎨
3. Knowledge in consuming API's using C#
4. Angular basics to visualize things

### Getting started

1. Create a simple [ASP.NET](http://asp.NET) Web application using the default template. This will provide you with a weather forecast controller
2. Create a simple angular app using ng new `signalR-client`
3. Since we know that any angular app by default will run in the `port 4200` let's solve the CORS issue first. If you have experience in creating apps earlier you knew this is the first most thing to be done for the client-server communication to happen.

Add the below piece to your startup to be able to communicate with the client without any issues.

```csharp
services.AddCors(options => 
    { 
        options.AddPolicy("CorsPolicy", builder => builder
        .WithOrigins("http://localhost:4200")
        .AllowAnyMethod()
        .AllowAnyHeader()
        .AllowCredentials()); 
    });
```

Please note that we are not using the `AllowAnyOrigin()` method to enable cors from any origin, but we explicitly say which origin to allow `WithOrigins(“[http://localhost:4200](http://localhost:4200/)”)`. We are doing this because in .NET Core 3.0 the combination of `AllowAnyOrigin` and `AllowCredentials` is considered as an insecure CORS configuration. 

### Server side code

Now we need to bring some data. How do we do that? As we will need Live BitCoin price, we will do with the `coindesk` API. Let's create a simple service to obtain data from the coindesk API and convert it based on our response model.

Since by default the coindesk API response gives out results for three countries. 

- USD
- GBP
- EUR

We will plot charts for all three of them. Just for convenience, I am converting the response into a JSON object like this.

```csharp
public class CoinResponse {
    public string Name { get; set; }
    public List<ReportResult> ReportData { get; set; }
}
public class ReportResult {
    public string Group { get; set; }
    public string Count { get; set; }
}
```

We are grouping each currency based on the currency code and the price. Now that we have a simple service to get data, let's look at the signalR stuff to be able to send data to the client-side in real-time.

### Adding SignalR 

We need to use the `Hub` class from the `Microsoft.AspNetCore.SignalR` and create a class like this

```csharp
public class CoinHub : Hub{
}
```

What is this Hub? Why do we need it? And why is the class empty?

Hub is nothing but a communication pipeline between client and server using SignalR. We don't need any methods here, because we are just doing `one-way` communication, where the server will send data to the client.

### Adding a controller

Now that we have everything in place, the last part is to add an Endpoint. Let's call the controller as `CoinController` and we will be using the `IHubContext` and call our service which fetches data from the coindesk API.

The controller will look like this. But notice something called `TimerManager` .

```csharp
[Route("api/coinPrice")]
[ApiController]
public class CoinController : ControllerBase
{
  private readonly IHubContext<CoinHub> _hub;

  public CoinController(IHubContext<CoinHub> hub)
  {
    _hub = hub;
  }

  public IActionResult Get()
  {
    var timerManager = new TimerManager(() => _hub.Clients.All.SendAsync("getCoinPrice", CoinService.GetCoinPrice()));
    return Ok(new { Message = "Request Completed" });
  }
}
```

Once you have added a controller don't forget to add the endpoint to your startup pointing to the empty Hub class that we created

```csharp
app.UseEndpoints(endpoints =>
  {
    endpoints.MapControllers();
    endpoints.MapHub<CoinHub>("/coinPrice");
  });
```

The route in the `MapHub` should match the controller endpoint path that we created above.

---

### TimerManager

This is a class to be able to repeatedly poll for whatever interval that you specify and send results to the client. Here I am adding a timer for every 10 seconds. You can modify the interval as per your needs.

```csharp
public class TimerManager {
    private readonly Timer _timer;
    private readonly AutoResetEvent _autoResetEvent;
    private readonly Action _action;

    public DateTime TimerStarted { get; }

    public TimerManager(Action action)
    {
      _action = action;
      _autoResetEvent = new AutoResetEvent(false);
      _timer = new Timer(Execute, _autoResetEvent, 10000, 10000);
      TimerStarted = DateTime.Now;
    }

    public void Execute(object stateInfo)
    {
      _action();

      if ((DateTime.Now - TimerStarted).Seconds > 60)
      {
        _timer.Dispose();
      }
    }
  }
}
```

### Enabling Angular client to consume SignalR

1. The first thing to do is install the signalR library. You can do it by running 

    ```bash
    npm i @aspnet/signalr
    ```

2. Now let's create a service to consume data using the SignalR Hub

```ts
private _hubConnection: signalR.HubConnection

  public startConnection() {
    this._hubConnection = new signalR.HubConnectionBuilder()
      .withUrl('https://localhost:44316/coinPrice').build();

    this._hubConnection.start()
      .then(() => console.log('connection started'))
      .catch(error => console.log('Error while creating connection:' + error));
  }

  public addCoinPriceListener = () => {
    this._hubConnection.on('getCoinPrice', (response) => {
      this.data = response;
      this.convertDateToTimeStamp();
    })
  }
```

- The code above is self-explanatory and very simple to understand. We are using the `SignalRHubConnectionBuilder` with our endpoint URL and we start the connection.
- Once we have the connection started using the connection call the hub's method. From the server-side, on the `Get` method in the controller, we can give the method name as `getCoinPrice` and obtain the response.
- Now the coindesk results response has only the date string. We need to convert that into a realTime object with an interval in minutes. I have written a simple method to map the result

```ts
convertDateToTimeStamp() {
    this.data.filter(x => {
      x.reportData.filter(r => {
        var timeStamp = moment.utc(r.group).toDate();
        const dataInDecimal = r.count.replace(/\,/g, '');
        var type = '';
        if (x.name === 'USD') {

        }
        // plot the data only when it has changed
        if (dataInDecimal != this.dataInDecimalcopy) {
          const dataCopy = this.series.find(s => s.name === x.name).data.slice(0);
          const timeCopy = this.timestamps.find(t => t.name === x.name).timedata.slice(0);
          dataCopy.push(parseFloat(dataInDecimal))
          timeCopy.push(timeStamp.getHours() + ":" + timeStamp.getMinutes() + ":" + timeStamp.getSeconds())
          this.dataInDecimalcopy = +dataInDecimal;

          // *optional: limit amount of data points shown
          if (dataCopy.length > 20) { dataCopy.shift(); }
          if (timeCopy.length > 20) { timeCopy.shift(); }

          // set the OG data equal to the copy
          this.series.find(s => s.name === x.name).data = dataCopy;
          this.timestamps.find(t => t.name === x.name).timedata = timeCopy;
        }
      });

    });
  }
```

Once we have done that, you can use any Chart Library or a simple table to visualize the data. Here I will be using `KendoCharts` to visualize and plot data. The HTML code for KendoCharts may look like this.

```html
<kendo-chart>
    <kendo-chart-title text="Bitcoin live pricing chart"></kendo-chart-title>
    <kendo-chart-legend position="bottom"
                        orientation="horizontal"></kendo-chart-legend>
    <kendo-chart-tooltip format="{0}"></kendo-chart-tooltip>
    <kendo-chart-value-axis>
      <kendo-chart-value-axis-item [title]="{ text: 'Price' }"
                                   [min]="6000"
                                   [max]="10000">
      </kendo-chart-value-axis-item>
    </kendo-chart-value-axis>
    <kendo-chart-category-axis>
      <kendo-chart-category-axis-item [title]="{ text: 'Time stamps' }"
                                      [categories]="_coinService.timestamps[0].timedata">
      </kendo-chart-category-axis-item>
    </kendo-chart-category-axis>
    <kendo-chart-series>
      <kendo-chart-series-item *ngFor="let item of _coinService.series"
                               [style]="'smooth'"
                               type="line"
                               [line]="{ style:'smooth' }"
                               [data]="item.data"
                               [name]="item.name">
      </kendo-chart-series-item>
    </kendo-chart-series>
  </kendo-chart>
```

Once you have done that you will be able to visualize live bitcoin prices like this. You can also change the type of chart. Here I have used `Line` chart, you may also use `Area`, `Bar` :chart:

[](livedata.gif)

## Conclusion 

I hope this blog gives you an understanding of using signalR to your own use cases. You can also try out various scenarios to be able to get more familiar with SignalR :boom:

This was originally posted on [Hariharan Subramanian](https://hariharan-subramanian.netlify.app/signalR-and-angular-live-data)