/Angular

How to visualize live data using SignalR and Angular

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

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.

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

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

Adding a controller

Now that we have everything in place, the last part is to add a 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 .

[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 forgot to add the endpoint to your startup pointing to the empty Hub class that we created

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.

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. First thing to do is install the signalR library. You can do it by running

    npm i @aspnet/signalr
  2. Now let’s create a service to consume data using the SignalR Hub
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 given 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 realTime object with interval in minutes. I have written a simple method to map the result
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 looks like this.

<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 emoji-chart

Live BitCoin Feed

Conclusion

I hope this blog gives you a understanding about using signalR to your own use cases. You can also try out various scenarious to be able to get more familiar with SignalR emoji-boom

HariHaran

HariHaran

Full Stack Developer

Read More