3 minute read

image

This is part 2 of this blog post and I really encourage you to read it before continuing reading this blog post.

Anyway the last thing to add to my small Taskmanager was AngularJS. I started to think about how to get SignalR and AngularJS working together and I got some real good pointers from the post “A Better Way of Using ASP.NET SignalR With Angular JS”.

I had another challenge with my existing code like the following statement in the JavaScript:

var chart = new Chart(document.getElementById("canvas").getContext("2d")).Line(lineChartData, options);

I could just move this code into my AngularJS controller but that seemed very ugly. So I found someone that already wrapped ChartJS in AngularJS directives into a small JavaScript library called Angles. So now when I was done reading others blog posts I felt ready to move on.

The AngularJS magic

Firstly I just pulled down AngularJS.Core and bootstrap NuGet packages.

  • Install-Package AngularJS.Core
  • Install-Package bootstrap

Then I added my own app.js JavaScript file that would contain my small application.

image

So far everything is pretty straightforward don’t you think?

Creating the SignalR connection in AngularJS

From the the post mentioned earlier I concluded that I needed to create a AngularJS service to hook up all my SignalR magic. The reason for this is that AngularJS services are singletons and that sounds perfect for this implementation.

var taskManagerApp = angular.module("taskManagerApp", ["angles"]).service("signalRSvc", function ($rootScope) {
  var initialize = function () {
    var cpuHub = $.connection.cpuHub;

    cpuHub.client.cpuInfo = function (machineName, cpu) {
      $rootScope.$emit("cpuInfo", machineName, cpu);
    };

    $.connection.hub.start();
  };

  return {
    initialize: initialize,
  };
});

Some important points in the code snippet above:

  • [“angles”], tells my AngularJS app to use the Angles library
  • .service, tells my AngularJS app that the next thing is a service named signalRSvc
  • the $rootScope.$emit, tells my AngularJS app to broadcast the current machineName and cpu to my controller.

The AngularJS Controller

Lets move on to the Controller code, this is where we control our view which is my index.html.

.controller("ChartController", function ($scope, signalRSvc, $rootScope) {
  $scope.machineName = "localhost";
  $scope.cpuChartLabel = "Total % Processor Time";
  $scope.lineChartData = {
    labels: [""],
    datasets: [
      {
        fillColor: "rgba(241,246,250,0.5)",
        strokeColor: "rgba(17,125,187,1)",
        pointColor: "rgba(17,125,187,1)",
        pointStrokeColor: "#fff",
        data: [0],
      },
    ],
  };

  $scope.options = {
    //Boolean - If we show the scale above the chart data
    scaleOverlay: false,

    //Boolean - If we want to override with a hard coded scale
    scaleOverride: true,

    //\*\* Required if scaleOverride is true \*\*
    //Number - The number of steps in a hard coded scale
    scaleSteps: 10,
    //Number - The value jump in the hard coded scale
    scaleStepWidth: 10,
    //Number - The scale starting value
    scaleStartValue: 0,

    //String - Colour of the scale line
    scaleLineColor: "rgba(0,0,0,.1)",

    //Number - Pixel width of the scale line
    scaleLineWidth: 1,

    //Boolean - Whether to show labels on the scale
    scaleShowLabels: true,

    //Interpolated JS string - can access value
    scaleLabel: "<%=value%>",

    //String - Scale label font declaration for the scale label
    scaleFontFamily: "'Arial'",

    //Number - Scale label font size in pixels
    scaleFontSize: 12,

    //String - Scale label font weight style
    scaleFontStyle: "normal",

    //String - Scale label font colour
    scaleFontColor: "#666",

    ///Boolean - Whether grid lines are shown across the chart
    scaleShowGridLines: true,

    //String - Colour of the grid lines
    scaleGridLineColor: "rgba(0,0,0,.05)",

    //Boolean - Whether the line is curved between points
    bezierCurve: false,

    //Boolean - Whether to show a dot for each point
    pointDot: false,

    //Boolean - Whether to animate the chart
    animation: false,
  };

  signalRSvc.initialize();

  var updateChartData = function (machineName, cpu) {
    if ($scope.lineChartData.labels.length > 20) {
      $scope.lineChartData.labels.shift();
    }

    $scope.lineChartData.labels.push("");

    if ($scope.lineChartData.datasets[0].data.length > 20) {
      $scope.lineChartData.datasets[0].data.shift();
    }

    $scope.lineChartData.datasets[0].data.push(cpu);
  };

  $scope.$parent.$on("cpuInfo", function (e, machineName, cpu) {
    $scope.$apply(function () {
      $scope.machineName = machineName;
      updateChartData(machineName, cpu);
    });
  });
});

Some important points in the code snippet above:

  • .controller, tells my AngularJS app that the next thing is a controller named ChartController and it uses a function that takes our service signalRSvc as a parameter.
  • signalRSvc.initialize();, tells my AngularJS app to initiate the SignalR connection.
  • $scope.$parent.$on("cpuInfo", function (e, machineName, cpu), tells my AngularJS to listen to calls from “cpuInfo” and this will then call into the updateChartData

The view binding it all together

Having done all the hard lifting from index.html to app.js file we end up with a very simple index.html like so

<div class="container">
  <div ng-app="taskManagerApp">
    <div class="jumbotron" ng-controller="ChartController">
      <h1></h1>
      <div class="span" %>
        <h3></h3>
        <canvas id="lineChart" data="lineChartData" options="options" linechart> </canvas>
      </div>
    </div>
  </div>

  <!-- Placed at the end of the document so the pages load faster -->
  <script src="Scripts/jquery-1.10.2.min.js"></script>
  <script src="Scripts/jquery.signalR-2.0.0.min.js"></script>
  <script src="Scripts/angular.min.js"></script>
  <script src="Scripts/bootstrap.min.js"></script>
  <script src="Scripts/chart.min.js"></script>
  <script src="Scripts/angles.js"></script>
  <script src="Scripts/app/app.js"></script>
  <script src="/signalr/hubs"></script>
</div>

I sure learned a lot from this very small SPA and I hope that some of you have too.

Cheers,

Hugo

Comments