В моем
последнем посте мы увидели, как использовать
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>
Откройте эту страницу в браузере и оригинальную страницу биржевого индикатора в другом окне браузера и играйте с помощью кнопок. Вы увидите, что оба экрана имеют одинаковые данные в любой момент времени. Единственным отличием будет состояние кнопок, их цвет и список прокрутки. Мы исправим их в следующем посте.
Удачного кодирования!