В моем
последнем посте мы увидели, как использовать
ASP.NET SignalR и
AngularJS вместе на примере типа «Hello World». Теперь, когда у нас есть некоторое представление о том, как использовать обе технологии вместе, мы рассмотрим более сложный сценарий, чтобы заставить фреймворки работать лучше вместе.
Я предполагаю, что вы уже видели образец тикера SignalR Stock . Если нет, загрузите его с GitHub или добавьте пакет NuGet в существующее веб-приложение ASP.NET. Убедитесь, что вы потратили некоторое время, чтобы запустить образец хотя бы один раз в нескольких браузерах и взгляните на код, прежде чем продолжить.
Я надеюсь, что вы ознакомились с кодом как на серверной, так и на клиентской стороне примера биржевого тикера. Мы не будем вносить никаких изменений в код сервера и макет страницы, но мы перепишем часть JavaScript, используя возможности AngularJS. Поскольку существует много кода для преобразования на стороне клиента, давайте сделаем это в серии из двух частей:
- Создание пользовательского сервиса для связи с концентратором на сервере и использование сервиса в контроллере (этот пост)
- Выполнение изменений пользовательского интерфейса на странице, таких как включение / отключение кнопок, прокрутка значений запасов в списке и добавление эффекта анимации к значениям в таблице и списке (следующий пост)
Сделайте копию файла StockTicker.html и дайте ему имя по вашему выбору. Добавьте в проект два файла JavaScript, controller.js и factory.js. Мы скоро добавим скрипт в эти файлы. Измените справочный раздел сценариев на странице, включив в него следующие файлы сценариев:
<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>
Давайте начнем реализовывать часть SignalR внутри пользовательского сервиса. Чтобы освободить контроллер от каких-либо действий, кроме предоставления данных для представления и обработки событий представления, мы создаем пользовательский сервис для обработки логики связи концентратора. Сервис отвечает за:
- Создание объектов, необходимых для общения
- Настройка клиентских функций для прокси-сервера и реагирования при открытии, закрытии или сбросе рынка или при обновлении стоимости акций
- Запуск соединения с концентратором
- Получение текущих значений акций и текущих рыночных статусов акций после начала подключения
- Открытие, закрытие или сброс рынков по требованию
Ниже приведены модуль и фабрика, которая обрабатывает функции, упомянутые выше:
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;
} ]);
Нам нужен контроллер, чтобы начать работу. Контроллер будет иметь следующие компоненты:
- Массив для хранения текущих значений запасов и логическое значение для хранения текущего состояния рынка
- Сеттеры для присвоения значений полям
- Функция для изменения значения записи в массиве запасов
- Функции для обработки операций открытия, закрытия и сброса при нажатии соответствующей кнопки
- Установите обратные вызовы для службы и попросите службу отключить связь
Ниже приведен код в контроллере:
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();
}
Макет HTML-страницы останется без изменений. Но нам нужно изменить способ отображения данных на экране. Образец биржевого тикера использует шаблонную технику бедного человека для визуализации содержимого в таблице и в списке прокрутки. Поскольку мы используем AngularJS, давайте заменим его выражениями. Ниже приведена разметка на странице в стиле Angular:
<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>
Откройте эту страницу в браузере и оригинальную страницу биржевого индикатора в другом окне браузера и играйте с помощью кнопок. Вы увидите, что оба экрана имеют одинаковые данные в любой момент времени. Единственным отличием будет состояние кнопок, их цвет и список прокрутки. Мы исправим их в следующем посте.
Удачного кодирования!