My very own Taskmanager in HTML 5, SignalR, Nancy and AngularJS
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.
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 namedChartController
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