Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Implementing a SignalR Stock Ticker Using AngularJS: Part2

DZone's Guide to

Implementing a SignalR Stock Ticker Using AngularJS: Part2

· Web Dev Zone
Free Resource

Prove impact and reduce risk when rolling out new features. Optimizely Full Stack helps you experiment in any application.

In the last post, we implemented the SignalR part of the stock ticker sample using AngularJS and modified the HTML page to use Angular’s directives. As promised earlier, we will finish the implementation in this post by incorporating UI changes using directives and filters.

AngularJS is designed with separation of concerns in mind. One of the best architectural features of AngularJS is that it makes it easier to separate HTML and JavaScript from each other while they play together at the same time. Features like directives and filters can be used to easily extend Angular’s expressions and behavior of HTML elements without wasting a lot of time and energy.


In the original Stock Ticker sample, the following changes are made while binding data on the page:

  • A change in a stock's value is prepended with an up or down symbol based on the value
  • The percentage value is multiplied by 100 and a "%" symbol is appended to it

It also has the following UI effects on the page:

  • jQuery color flash effect on rows of tables and list items
  • List item keeps scrolling when the stock market is open
  • Buttons are enabled and disabled based on the current market state

Creating Filters to Represent Data More Effectively

Let’s write a couple of filters to manipulate change and percent change while presenting on the screen:

app.filter('percentage', function () {
    return function (changeFraction) {
        return (changeFraction * 100).toFixed(2) + "%";
    }
});
  
app.filter('change', function () {
    return function (changeAmount) {
        if (changeAmount > 0) {
            return "▲ " + changeAmount.toFixed(2);
        }
        else if (changeAmount < 0) {
            return "▼ " + changeAmount.toFixed(2);
        }
        else {
            return changeAmount.toFixed(2);
        }
    }
});
As you see in the above code, the implementation of the filters is pretty straightforward. The value on which the filter has to act is passed into the filter function. All we need to do is manipulate the value as needed and return it. Using these filters is very easy as well--the following snippet demonstrates the usage:
<td>
    {{stock.Change | change}}
</td>
<td>
    {{stock.PercentChange | percentage}}
</td>
Creating Directives to Make the UI Better

The buttons labeled "open market" and "reset" have to be enabled when the market is closed and the "close market" button has to be enabled when the market is opened. We can simulate this behavior using the Boolean property marketIsOpen inside the expression ({{ }}).

To evaluate expressions inside a directive, we need the $interpolate service. The directive also needs to watch for changes in the value in order to immediately change the state of the button. Following is the implementation of the directive:

app.directive('disable', function ($interpolate) {
    return function (scope, elem, attrs) {
        var exp = $interpolate(elem.attr('data-disable'));
        function updateDisabled() {
            var val = scope.$eval(exp);
            if (val == "true") {
                elem[0].disabled = 'disabled';
            }
            else {
                elem[0].disabled = '';
            }
        }
  
        scope.$watch(exp, function (value) {
            updateDisabled();
        });
    }
});
While using the directive, we should assign it with a Boolean expression.
<input type="button" id="open" value="Open Market" data-disable="{{marketIsOpen}}"
            data-ng-click="openMarket()" />
<input type="button" id="close" value="Close Market" data-disable="{{!marketIsOpen}}"
            data-ng-click="closeMarket()" />
To indicate a change in a stock's value, the corresponding row in the table should flash. Let’s write a directive to do this:
app.directive('flash', function ($) {
    return function (scope, elem, attrs) {
        var flag = elem.attr('data-flash');
        var $elem = $(elem);
  
        function flashRow() {
            var value = scope.stock.LastChange;
            var changeStatus = scope.$eval(flag);
            if (changeStatus) {
                var bg = value === 0
                            ? '255,216,0' // yellow
                            : value > 0
                            ? '154,240,117' // green
                            : '255,148,148'; // red
  
                $elem.flash(bg, 1000);
            }
        }
  
        scope.$watch(flag, function (value) {
            flashRow();
        });
    }
});
The directive can be applied on any element that needs the scroll effect. Following is a sample table row using this directive:
<tr data-ng-repeat="stock in stocks" data-flash="marketIsOpen">
 ...
 ...
</tr>
The final directive that we have to create will be used to make the stock list scrollable. Again, values will keep scrolling when the market is opened. Following is the implementation:
app.directive('scrollTicker', function ($) {
    return function (scope, elem, attrs) {
        var $scrollTickerUI = $(elem);
        var flag = elem.attr('data-scroll-ticker');
        scroll();
  
        function scroll() {
            if (scope.$eval(flag)) {
                var w = $scrollTickerUI.width();
                $scrollTickerUI.css({ marginLeft: w });
                $scrollTickerUI.animate({ marginLeft: -w }, 15000, 'linear', scroll);
            }
            else
                $scrollTickerUI.stop();
        }
  
        scope.$watch(flag, function (value) {
            scroll();
        });
    }
});
It is applied on the list as:
<ul data-scroll-ticker="marketIsOpen">
 ...
 ...
</ul>
Now open the new scroll ticker page on a browser. Open the original jQuery-based implementation on another browser window and play with both the screens. Now both of them have the same look and behavior.


This sample demonstrates how to make the best use of the features of AngularJS with SignalR to create real-time data-oriented applications. The beauty of this approach is that we have several independent components playing pretty well together. This approach makes it easier to organize the code and makes each component testable in isolation.

The code covered in this series is available on GitHub. Feel free to fork the code if you think that any part of it can be improved!

Happy coding!

With SDKs for all major client and server side platforms, you can experiment on any platform with Optimizely Full Stack.

Topics:

Published at DZone with permission of Rabi (ravi) Kiran, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}