In my last two posts, I experimented with building the Sieve of
Eratosthenes in
JavaScript.
I wanted to provide a visualization of the algorithm so I used CSS
animations to indicate changes in state. At first I did this using
timeouts to show the execution after a delay, which meant my code was not using recursion as you might expect
from the algorithm. When I moved to keeping things isolated in a queue
of states and then iterating over those states, the algorithm looked
better, but AngularJS did not know that my states were connected and
could not apply the proper
classes
to display the animations. I’ve been refactoring this for a bit and
have a good solution now which preserves the animations and allows the
original algorithm to run without timeout callbacks.
The way it works is by exposing an AngularJS service which stores the
states. When a state change occurs, you call into this service and
store that state. The service then invokes its own callback thread to
iterate over the states and set the “current state” to the first item
off the queue. Inside your controller you store a reference to the
current state, and then use that reference inside your HTML template.
As the service updates the states, you’ll see the changes reflected in
your template.
In order to take advantage of AngularJS dirty checking, the
Algorithm intelligently copies objects into the state queue. If the
object is an array of objects, it makes sure to copy each key of the
object, rather than just the entire object. This allows AngularJS to
know that the object has changed and apply the CSS classes to show
animation. You can see this in the function
addArrayItemToWorldState. Also, note that this service has no clue
what items I am putting into the state. You push the items into the
state queue and then know that the_state will have the net one in
the sequence ready for you in your template.
A sample calling pattern might be:
var mod = angular.module( "MyModule" , [ 'AnimateAlgorithms' ] );
mod.controller( "MyAlgorithmCtrl", [ '$scope', 'Algorithm',
function( $scope, Algorithm ) {
$scope.start = function() {
$scope.state = Algorithm.the_state;
$scope.animate( 250 ); // Iterate over 250 ms, starting two seconds after this is called
$scope.save( { "p" : $scope.p, "numbers": $scope.numbers } );
$scope.runLongCalculation();
$scope.save( { "p" : $scope.p, "numbers": $scope.numbers } );
}
} ] );