In my last post, we saw how to use ASP.NET SignalR and Angular JS together using a Hello World type of example. Now that we got some idea on using both the technologies together, we will take a look at a more advanced scenario to make the frameworks work better together.
I assume that you have already seen the SignalR Stock ticker sample. If not, download it from Github or add the NuGet Package to an existing ASP.NET web application. Make sure that you take some time to run the sample at least once on multiple browsers and have a glance at the code before you proceed further.
I hope you had a look at the code on both server and client side of the stock ticker sample. We will not make any modification to the server code and the layout of the page. But we will rewrite the JavaScript part using features of Angular JS. As there is a lot of client side code to convert, let’s do it in a two-part series:
Make a copy of the StockTicker.html file and give it a name of your choice. Add two files JavaScript files, controller.js and factory.js to the project. We will add script to these files in sometime. Modify the script reference section of the page to include the following script files:
Following is the code in the controller:
Layout of the HTML page will remain unchanged. But we need to change the way data is rendered on the screen. The stock ticker sample uses poor man’s template technique to render content in the table and in the scrolling list. Since we are using Angular JS, let’s replace it with expressions. Following is the mark-up on the page in Angular’s style:
Open this page on a browser and the original stock ticker page on another browser window and play with the buttons. You will see both the screens with same data at any given point of time. The only difference would be the state of buttons, colour and scrolling list. We will fix them in the next post.
Happy coding!
I assume that you have already seen the SignalR Stock ticker sample. If not, download it from Github or add the NuGet Package to an existing ASP.NET web application. Make sure that you take some time to run the sample at least once on multiple browsers and have a glance at the code before you proceed further.
I hope you had a look at the code on both server and client side of the stock ticker sample. We will not make any modification to the server code and the layout of the page. But we will rewrite the JavaScript part using features of Angular JS. As there is a lot of client side code to convert, let’s do it in a two-part series:
- Creating a custom service to communicate with hub on the server and using the service in a controller (This post)
- Performing UI changes on the page like enabling/disabling buttons, scrolling stock values in a list, adding animation effect to values in table and list (Next post)
Make a copy of the StockTicker.html file and give it a name of your choice. Add two files JavaScript files, controller.js and factory.js to the project. We will add script to these files in sometime. Modify the script reference section of the page to include the following script files:
<script src="../../bundles/jquery"></script>
<script src="../../bundles/jquerycolor"></script>
<script src="../../Scripts/jquery.signalR-1.0.1.js"></script>
<script type="text/javascript" src="../../Scripts/angular.js"></script>
<script type="text/javascript" src="factory.js"></script>
<script type="text/javascript" src="controller.js"></script>
Let’s start implementing SignalR part inside a custom service. To keep the controller free from doing anything other than providing data to the view and handle view events, we are creating a custom service to handle the hub communication logic. The service is responsible for:
- Creating objects needed for communication
- Configuring client functions to proxy to respond when market is opened, closed, reset or a stock value is updated
- Starting a hub connection
- Getting current values of stocks and current market status once the connection is started
- Opening, closing or resetting market on demand
var app = angular.module('app', []);
app.value('$', $);
app.factory('stockTickerData', ['$', '$rootScope', function ($, $rootScope) {
function stockTickerOperations() {
//Objects needed for SignalR
var connection;
var proxy;
//To set values to fields in the controller
var setMarketState;
var setValues;
var updateStocks;
//This function will be called by controller to set callback functions
var setCallbacks = function (setMarketStateCallback, setValuesCallback, updateStocksCallback) {
setMarketState = setMarketStateCallback;
setValues = setValuesCallback;
updateStocks = updateStocksCallback;
};
var initializeClient = function () {
//Creating connection and proxy objects
connection = $.hubConnection();
proxy = connection.createHubProxy('stockTicker');
configureProxyClientFunctions();
start();
};
var configureProxyClientFunctions = function () {
proxy.on('marketOpened', function () {
//set market state as open
$rootScope.$apply(setMarketState(true));
});
proxy.on('marketClosed', function () {
//set market state as closed
$rootScope.$apply(setMarketState(false));
});
proxy.on('marketReset', function () {
//Reset stock values
initializeStockMarket();
});
proxy.on('updateStockPrice', function (stock) {
$rootScope.$apply(updateStocks(stock));
});
};
var initializeStockMarket = function () {
//Getting values of stocks from the hub and setting it to controllers field
proxy.invoke('getAllStocks').done(function (data) {
$rootScope.$apply(setValues(data));
}).pipe(function () {
//Setting market state to field in controller based on the current state
proxy.invoke('getMarketState').done(function (state) {
if (state == 'Open')
$rootScope.$apply(setMarketState(true));
else
$rootScope.$apply(setMarketState(false));
});
});
};
var start = function () {
//Starting the connection and initializing market
connection.start().pipe(function () {
initializeStockMarket();
});
};
var openMarket = function () {
proxy.invoke('openMarket');
};
var closeMarket = function () {
proxy.invoke('closeMarket');
};
var reset = function () {
proxy.invoke('reset');
};
return {
initializeClient: initializeClient,
openMarket: openMarket,
closeMarket: closeMarket,
reset: reset,
setCallbacks: setCallbacks
}
};
return stockTickerOperations;
} ]);
We need a controller to start the work. The controller will have following components:
- An array to store current stock values and a Boolean value to store current market state
- Setters to assign values to the fields
- A function to modify the value of an entry in the stocks array
- Functions to handle open, close and reset operations when corresponding button is clicked
- Set callbacks to the service and ask the service to kick off the communication
Following is the code in the controller:
var StockTickerCtrl = function ($scope, stockTickerData) {
$scope.stocks = [];
$scope.marketIsOpen = false;
$scope.openMarket = function () {
ops.openMarket();
}
$scope.closeMarket = function () {
ops.closeMarket();
}
$scope.reset = function () {
ops.reset();
}
function assignStocks(stocks) {
$scope.stocks = stocks;
}
function replaceStock(stock) {
for (var count = 0; count < $scope.stocks.length; count++) {
if ($scope.stocks[count].Symbol == stock.Symbol) {
$scope.stocks[count] = stock;
}
}
}
function setMarketState(isOpen) {
$scope.marketIsOpen = isOpen;
}
var ops = stockTickerData();
ops.setCallbacks(setMarketState, assignStocks, replaceStock);
ops.initializeClient();
}
Layout of the HTML page will remain unchanged. But we need to change the way data is rendered on the screen. The stock ticker sample uses poor man’s template technique to render content in the table and in the scrolling list. Since we are using Angular JS, let’s replace it with expressions. Following is the mark-up on the page in Angular’s style:
<div data-ng-app="app" data-ng-controller="StockTickerCtrl">
<h1>
ASP.NET SignalR Stock Ticker Sample</h1>
<input type="button" id="open" value="Open Market" data-ng-click="openMarket()" />
<input type="button" id="close" value="Close Market" data-ng-click="closeMarket()" />
<input type="button" id="reset" value="Reset" data-ng-click="reset()" />
<h2>
Live Stock Table</h2>
<div id="stockTable">
<table border="1">
<thead>
<tr>
<th>Symbol</th>
<th>Price</th>
<th>Open</th>
<th>High</th>
<th>Low</th>
<th>Change</th>
<th>%</th>
</tr>
</thead>
<tbody>
<tr class="loading" data-ng-show="stocks.length==0">
<td colspan="7">
loading...
</td>
</tr>
<tr data-ng-repeat="stock in stocks">
<td>
{{stock.Symbol}}
</td>
<td>
{{stock.Price | number:2}}
</td>
<td>
{{stock.DayOpen | number:2}}
</td>
<td>
{{stock.DayHigh | number:2}}
</td>
<td>
{{stock.DayLow | number:2}}
</td>
<td>
{{stock.Change}}
</td>
<td>
{{stock.PercentChange}}
</td>
</tr>
</tbody>
</table>
</div>
<h2>
Live Stock Ticker</h2>
<div id="stockTicker">
<div class="inner">
<ul>
<li class="loading" data-ng-show="stocks.length==0">loading...</li>
<li data-ng-repeat="stock in stocks" data-symbol="{{stock.Symbol}}"><span class="symbol">
{{stock.Symbol}}</span> <span class="price">{{stock.Price | number:2}}</span><span
class="change">{{stock.Change}} ({{stock.PercentChange}})</span>
</li>
</ul>
</div>
</div>
</div>
Open this page on a browser and the original stock ticker page on another browser window and play with the buttons. You will see both the screens with same data at any given point of time. The only difference would be the state of buttons, colour and scrolling list. We will fix them in the next post.
Happy coding!
