Angular Property State Watcher and Setter Directive

Home / Angular Property State Watcher and Setter Directive

Ok, I couldn’t think of a good title for this post. But, I found this concept to be pretty interesting.

Basically, I wanted a directive that would allow me to watch AND set a property as if it were a state changing bool. This needs a bit more explaining, I’m sure.


By stateful watcher, imagine that I have a collection of objects, or even a single object, that has an arbitrary property called isError. I want to watch for when that property changes, set a CSS class on an underlying DOM element, and then, after a given period of time, reset the state of the property to a default value (zero/false in this case). This allows my original collection/objects to be modified without having to deal with eventing or callbacks from the directive back to the originating controller/scope. My idea for this arose with the need to toggle an error status on a bulk editable collection of items in a table such that a user could be notified when some, but not all, of their edits could be persisted without issue.

Jumping right into the code, I called the directive “isState.” You can pass in an “isStateClass” that represents the CSS class that gets applied and a timeout value to indicate how long to wait before the state is changed back. You can see that the “isState” property is watched, and a $timeout is used w/ the $animate service to remove or add the CSS class as appropriate. I used $animate so that my CSS classes could be used whether I’m using this directive or straight-up Angular animations.

(function () {
    var isStateDirective = function ($animate, $timeout) {
        var directive = {
            restrict: 'A',
            scope: {
                isState: '=',
                isStateClass: '=',
                isStateTimeout: '='
            },
            link: function (scope, element, attrs) {
                var
                    className = angular.isDefined(attrs.isStateClass) ? scope.isStateClass : 'is-state',
                    timeout = angular.isDefined(attrs.isStateTimeout) ? scope.isStateTimeout : 2000,
                    startAnimation = function () {
                        if (scope.isState) {
                            $animate.addClass(element, className);
                            $timeout(function () {
                                scope.isState = false;
                                $animate.removeClass(element, className);
                            }, timeout);
                        } else if (scope.isState === false) {
                            $animate.removeClass(element, className);
                        }
                    };

                // Watch the attribute to toggle
                scope.$watch('isState', function () {
                    startAnimation();
                });
            }
        };
        return directive;
    };
})()

Here’s a link to the Plunker: http://plnkr.co/edit/w876ka?p=info
Here’s a link to the Github source: https://github.com/long2know/angular-directives-general

In the Plunker demo, and the Codepen inline-demo below, you will see that I illustrate the directive and an ngAnimate approach. You may be wondering why I’d bother with this custom directive when ngAnimate can, effectively, do the same work. It all comes down to where I wanted to place the responsibility of toggling the state. In one case, it’s handled by the directive, and in the other, the page’s controller has to do the work. Off hand, I’m not sure if either approach has advantages in terms of performance, but I found both approaches worth looking into.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.