Creating Live Scoring using SignalR, BootStrap CSS, HMTL5 and jQuery

ASP.NET SignalR is a library for ASP.NET developers that enables real-time communication on the web or desktop. It uses WebSockets of HTML5 API to enable bi-directional communication between the browser and server.

Applications SignalR can build:
  • Chat
  • customer support
  • Gaming
  • Live Scoring
  • Live Trending (stock pricing, air flights, rates, exchange rates, survey)
  • and many more…
In this article, we will be building online Live Scoring. The Scores will be feed in by an administrator and will be viewed by any client connected to the web.

Where is the Finished Product?



Screen Shots

Let us take a look at the LiveScore Application that we will be building.


The Admin Panel The Viewer Panel
Technology Used
  1. Visual Studio 2012/2013 (I used VS 2012 here but your can use VS 2013)
  2. Microsoft.AspNet.SignalR (the version uses here is 2.1.0)
  3. Asp.Net Empty Web Application
  4. BootStrap.css
  5. BootStrap.js
  6. Glyphicons font
  7. jQuery
  8. CSS
  9. Html
How to get SignalR?

Go to Package Manager Console and type Install-Package Microsoft.AspNet.SignalR or go to Package Manager (Manage Nuget Package) and select Microsoft ASP.NET SignalR.

SignalR Version
The version we will using here is Microsoft.AspNet.SignalR 2.1.0.

How Do We Build It?

1. First, created new project, use C#, ASP.NET Empty Web Application.
2. Install the Microsoft.AspNet.SignalR.
3. Start coding.

The Server Side

Create the Startup.cs

This class will simply start the SignalR api.

Startup.cs
Create the Proxy Hub

This is the class that client sides call upon whenever it pushes data. The server(ScoreHub.cs) can also call and pushes data on the client side by invoking the Clients class.

Note: When calling Client methods in c#, it should be in camel case.
eg: Clients.Caller.getScore(_score);

ScoreHub.cs
using Microsoft.AspNet.SignalR;
using LiveScore.Model;
namespace LiveScore
{
public class ScoreHub : Hub
{
static Score _score = new Score();
public void Connect()
{
Clients.Caller.getScore(_score);
}
public void GameConfigure(Score Score)
{
_score.TeamName1 = Score.TeamName1;
_score.TeamName2 = Score.TeamName2;
_score.Team1Score = 0;
_score.Team2Score = 0;
SendScore();
}
public void UpdateScore(Score Score)
{
_score.TeamName1 = Score.TeamName1;
_score.Team1Score = Score.Team1Score;
_score.TeamName2 = Score.TeamName2;
_score.Team2Score = Score.Team2Score;
SendScore();
}
public void SendScore()
{
Clients.All.getScore(_score);
}
}
}

The Score.cs

This is a model where the score data will be stored.
Score.cs
namespace LiveScore.Model
{
public class Score
{
public string TeamName1 { get; set; }
public int Team1Score { get; set; }
public string TeamName2 { get; set; }
public int Team2Score { get; set; }
}
}


The Client Side

We need 2 Html files and 1 JavaScript file.
  • index.html = Viewers page
  • admin.html = Admin page
  • site.js  = Use in index.hml. It is use to communicate with the server(Hub Proxy) and vice versia.
  • admin.js  = Use in admin.html. It is use to to communicate with the server(Hub Proxy) and vice versia.

index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>LiveScore</title>
<link rel="stylesheet" href="/Css/JQueryUI/themes/base/jquery.ui.all.css" />
<link href="Css/bootstrap.min.css" rel="stylesheet" />
<link href="Css/amelia-bootstrap.min.css" rel="stylesheet" />
<link href="Css/footerBottom.css" rel="stylesheet" />
<link href="Css/Site.css" rel="stylesheet" />
<script src="/Scripts/jquery-1.8.2.min.js"></script>
<script src="Scripts/jquery.signalR-2.1.0.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="/signalr/hubs"></script>
</head>
<body>
<div id="wrapper">
<header>
<div class="navbar navbar-default" role="navigation">
<div class="container">
<div class="navbar-header">
<button class="btn btn-success navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="glyphicon glyphicon-chevron-down"></span>
</button>
<a class="navbar-brand" href="#">Live Score</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li class="nav active"><a href=".">Home</a>
</li>
<li class="nav"><a href="Admin.html">Admin</a>
</li>
<li class="nav"><a href="About.html">About</a>
</li>
</ul>
</div>
</div>
</div>
</header>
<div class="content container">
<div id ="boxScore">
<div class="form-horizontal col-md-6 col-sm-5 scoreBox box-shadow">
<div id="alertMsg" class="alert alert-warning collapse">
No Games today !!! Try refreshing later.
</div>
<div id="objectToAnimate">
<div class="form-group">
<div class="col-md-4 col-sm-3" id="Team1">
</div>
<div class="col-md-1 col-sm-3 col-xs-3" id="Score1">
</div>
</div>
<div class="form-group">
<div class="col-md-4 col-sm-3" id="Team2">
</div>
<div class="col-md-1 col-sm-3 col-xs-3" id="Score2">
</div>
</div>
</div>
</div>
</div>
</div>
<footer>
<div class="container float-left">
<p>&copy; Copyright CodexSquare</p>
</div>
</footer>
</div>
<script src="Scripts/bootstrap.min.js"></script>
<script src="Scripts/Site.js"></script>
</body>
</html>
admin.Html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>LiveScore Admin</title>
<link href="Css/bootstrap.min.css" rel="stylesheet" />
<link href="Css/amelia-bootstrap.min.css" rel="stylesheet" />
<link href="Css/footerBottom.css" rel="stylesheet" />
<script src="/Scripts/jquery-1.8.2.min.js"></script>
<script src="Scripts/jquery.signalR-2.1.0.min.js"></script>
<script src="/signalr/hubs"></script>
</head>
<body>
<div id="wrapper">
<header>
<div class="navbar navbar-default" role="navigation">
<div class="container">
<div class="navbar-header">
<button class="btn btn-success navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="glyphicon glyphicon-chevron-down"></span>
</button>
<a class="navbar-brand" href="#">Live Score</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li class="nav"><a href=".">Home</a>
</li>
<li class="nav active"><a href="Admin.html">Admin</a>
</li>
<li class="nav"><a href="About.html">About</a>
</li>
</ul>
</div>
</div>
</div>
</header>
<div class="content container">
<div id="alertMsg" class="alert alert-warning collapse">
</div>
<ul class="nav nav-tabs" id="LiveTabs">
<li><a href="#scoreBoardTab" data-toggle="tab">Scoreboard</a>
</li>
<li class="active"><a href="#settingsTab" data-toggle="tab">Settings</a>
</li>
</ul>
<div class="tab-content">
<div class="well tab-pane" id="scoreBoardTab">
<div class="form-horizontal">
<div class="form-group">
<div class="col-md-2" id="Team1NameScoreBoard">
<label for="Team1Score" class="control-label">Not set</label>
</div>
<div class="col-md-1 col-sm-4 col-xs-4">
<input type="number" id="Team1ScoreBoardScore" name="Team1ScoreBoardScore" class="form-control" />
</div>
</div>
<div class="form-group">
<div class="col-md-2" id="Team2NameScoreBoard">
<label for="Team2Score" class="control-label">Not set</label>
</div>
<div class="col-md-1 col-sm-4 col-xs-4">
<input type="number" id="Team2ScoreBoardScore" name="Team2ScoreBoardScore" class="form-control" />
</div>
</div>
</div>
<hr />
<div>
<input type="button" class="btn btn-success" id="SendScore" value="Send Score" />
</div>
</div>
<div class="well tab-pane active" id="settingsTab">
<div class="form-horizontal">
<div class="form-group">
<label for="TeamNameSet1" class="control-label col-md-2">Team</label>
<div class="col-md-3 col-sm-3">
<input type="text" id="TeamNameSet1" name="TeamNameSet1" class="form-control" required="required" />
</div>
</div>
<div class="form-group">
<div class="col-md-3 col-xs-3 col-md-offset-2" >
vs
</div>
</div>
<div class="form-group">
<label for="TeamNameSet2" class="control-label col-md-2">Team</label>
<div class="col-md-3 col-sm-3">
<input type="text" id="TeamNameSet2" name="TeamNameSet2" class="form-control" required="required" />
</div>
</div>
<div class="form-group">
<div class="col-md-3 col-md-offset-2" >
<input type="button" class="btn btn-success" id="ConfigBoard" value="Start" />
</div>
</div>
</div>
<hr />
</div>
</div>
</div>
<footer>
<div class="container float-left">
<p>&copy; Copyright CodexSquare</p>
</div>
</footer>
</div>
<script src="Scripts/bootstrap.min.js"></script>
<script src="Scripts/admin.js"></script>
</body>
</html>
The site.js

There is only one client method that the server can call (but you can add more) :

scoreHub.client.getScore

and there are 2 server methods that the site.js can call:
  • Connect
  • GameConfigure
  • UpdateScore
  • SendScore
Note: When calling Server methods in JavaScript, it should be in camel case.
eg: scoreHub.server.connect();

site.js
(function () {
"use strict";
var scoreHub;
var $alertMsg;
var alertHtmlClose = '<a href="#" data-dismiss="alert" class="close">&times;</a>';
var alertHtml;
$(function () {
$.ajaxSetup({
cache: false
});
scoreHub = $.connection.scoreHub;
configListeners();
// Start Hub
$.connection.hub.start({ transport: ['webSockets', 'serverSentEvents', 'longPolling'] })
.done(function () {
scoreHub.server.connect();
});
});
function configListeners() {
scoreHub.client.getScore = function (liveScore) {
$alertMsg.hide();
if (liveScore.TeamName1 == null) {
if (!$("#Team1").length) {
return false;
}
$("#objectToAnimate").hide();
$alertMsg.show();
return false;
}
$("#Team1").html(liveScore.TeamName1);
$("#Team2").html(liveScore.TeamName2);
$("#Score1").html(liveScore.Team1Score);
$("#Score2").html(liveScore.Team2Score);
$("#objectToAnimate").fadeOut(500);
$("#objectToAnimate").fadeIn(500);
};
$alertMsg = $("#alertMsg");
$alertMsg.hide();
$alertMsg.on("close.bs.alert", function () {
$alertMsg.hide();
return false;
});
}
})();



The admin.js

The admin use to configure game and send scores to viewers. There are 4 server methods that the admin.js can call:
  • Connect
  • GameConfigure
  • UpdateScore
  • SendScore

admin.js
(function () {
"use strict";
var scoreHub;
var liveScore = {"TeamName1": "", "Team1Score": 0, "TeamName2": "", "Team2Score": 0};
var TeamNameSet1;
var TeamNameSet2;
var $alertMsg;
var alertHtmlClose = '<a href="#" data-dismiss="alert" class="close">&times;</a>';
var alertHtml;
var $Team1Name = $("#TeamNameSet1");
var $Team2Name = $("#TeamNameSet2");
//For Admin
var $Team1NameScoreBoard = $("#Team1NameScoreBoard");
var $Team2NameScoreBoard = $("#Team2NameScoreBoard");
var $Team1ScoreBoardScore = $("#Team1ScoreBoardScore");
var $Team2ScoreBoardScore = $("#Team2ScoreBoardScore");
$(function () {
$.ajaxSetup({
cache: false
});
scoreHub = $.connection.scoreHub;
configListeners();
// Start Hub
$.connection.hub.start({ transport: ['webSockets', 'serverSentEvents', 'longPolling'] })
.done(function () {
scoreHub.server.connect();
});
});
function configListeners() {
scoreHub.client.getScore = function (_liveScore) {
$alertMsg.hide();
//Avoid empty data when switching pages
if (_liveScore.TeamName1 != null) {
//For the config
$Team1Name.val(_liveScore.TeamName1);
$Team2Name.val(_liveScore.TeamName2);
//For the Scoreboard
$Team1NameScoreBoard.html(_liveScore.TeamName1);
$Team2NameScoreBoard.html(_liveScore.TeamName2);
$Team1ScoreBoardScore.val(_liveScore.Team1Score);
$Team2ScoreBoardScore.val(_liveScore.Team2Score);
liveScore = _liveScore;
}
};
$("#SendScore").click(function () {
sendScore();
});
$("#ConfigBoard").click(function () {
conFigBoard();
});
$alertMsg = $("#alertMsg");
$alertMsg.hide();
$alertMsg.on("close.bs.alert", function () {
$alertMsg.hide();
return false;
});
}
function conFigBoard() {
TeamNameSet1 = $Team1Name.val();
TeamNameSet2 = $Team2Name.val();
if ((TeamNameSet1 == "") || (TeamNameSet2 == "")) {
alertHtml = alertHtmlClose;
alertHtml = alertHtml.concat('Team Names are empty. Please supply.');
$alertMsg.show();
$alertMsg.html(alertHtml);
return false;
}
$alertMsg.hide();
$Team1NameScoreBoard.html(TeamNameSet1);
$Team2NameScoreBoard.html(TeamNameSet2);
liveScore.TeamName1 = TeamNameSet1;
liveScore.Team1Score = 0;
liveScore.TeamName2 = TeamNameSet2;
liveScore.Team2Score = 0;
scoreHub.server.gameConfigure(liveScore);
$('#LiveTabs a[href="#scoreBoardTab"]').tab('show');
}
function sendScore() {
var isDefinedTeam1 = $Team2NameScoreBoard.html().toLowerCase().indexOf("not set");
var isDefinedTeam2 = $Team2NameScoreBoard.html().toLowerCase().indexOf("not set");
if ((isDefinedTeam1 >= 0) || (isDefinedTeam2 >= 0)) {
alertHtml = alertHtmlClose;
alertHtml = alertHtml.concat('Cannot send score. No teams has been defined.');
$alertMsg.show();
$alertMsg.html(alertHtml);
return false;
}
$alertMsg.hide();
var Team1Score = $Team1ScoreBoardScore.val();
var Team2Score = $Team2ScoreBoardScore.val();
liveScore.Team1Score = Team1Score;
liveScore.Team2Score = Team2Score;
scoreHub.server.updateScore(liveScore);
}
})();

Thank you! Hope this will help you make your web application "real time". Share it with friends...

3 comments

Hi! How i can add more games?

Reply

Thank For Sharing Your Information The Information Shared Is Very Valuable Please Keep Updating Us Time Went On Just Reading The Article Python Online Course

Reply