2013-12-01
My Little Machines Part 3

The current <machine> directive has a fixed concept of a termination condition, time, and what constitutes each step of a computation. We want to evolve this directive so that as much as possible is specifiable in HTML, enabling customization of machines.

Experiment A - Implementing a stepFn attribute

We’ll start by adding an attribute for the machine called stepFn, which contains an Angular expression that can do whatever it pleases, in the parent HTML context containing the <machine>. In this first example, we’ll just have an externally visible state variable customStateA that is modified whenever the machine undergoes a Step operation. So that it doesn’t look like a proxy for machineTime, we’ll make it a string and use concatenation. Because we don’t have a resetFn callback (yet), the customStateA will get bigger until page refresh.

What’s really interesting and powerful is that I’m managing my per-machine state in my HTML, whereas the reusable computational guts are hidden away in my directive. This is exactly what I’m looking for building My Little Machines™.

Also, I can have my machine manipulate state that is external to the directive itself, provided that state is passed in as an attribute to the machine (via ng-init or step-fn or other handlers that may eventually be added to <machine>).

Reset

Step Resume


Here is the code for the above:

<div>

<h4>Custom State: <span style="color:purple;font-size:200%;">{{customStateA}}</span></h4>

<machine ptr="myMachineA" ng-init="customStateA = '-'" step-fn="customStateA = customStateA + '+'" style="background-color:#777777;">

<h6 style="color:#11DD11;" class="pull-left">Time: {{ myMachineA.getMachineTime() }}</h6>
<h6 style="color:#11DD11;" class="pull-right">State: {{ myMachineA.getMachineState() }}</h6>
<br/>

<hr style="height:50px; width:{{ 25 * (myMachineA.getMachineTime() + 1) }}px; background-color:{{ myMachineA.isRunning() ? 'green' : 'red' }};"/>

<button ng-click="myMachineA.resetMachine()">Reset</button>

<button ng-disabled="!myMachineA.isRunning()" ng-click="myMachineA.stepMachine()">Step</button>
<button ng-disabled="!myMachineA.isRunning() || myMachineA.isPaused()" ng-click="myMachineA.resumeMachine()">Resume</button>

</machine>
</div>

Experiment B - Changing Machine State

It would be nice to be able to have our HTML-based machine determine when the underlying machine should change state. Let’s try to do that in the step-fn, where we’ll Pause the machine if the machineTime == 5. Using the Resume command after this will continue execution.

We’ll also use this example to add an explicit Pause button, as well as tightening up the styling on the machine UI, to preserve page space.

Reset Step Resume Pause


Here is the code for the above:

<h4>Custom State: <span style="color:purple;font-size:150%;">{{customStateB}}</span></h4>

<machine ptr="MB" ng-init="customStateB = '-'"
	step-fn="(MB.getMachineTime() == 5) ? (MB.pauseMachine()) : (customStateB = customStateB + '+')"
	class="mlm background">

<h6 class="status left">Time: {{ MB.getMachineTime() }}</h6>
<h6 class="status right">State: {{ MB.getMachineState() }}</h6>

<hr class="mlm-timebar" ng-class="MB.isRunning() ? 'running' : 'halted'" style="width:{{ 20 * (MB.getMachineTime() + 1) }}px;"/>

<button class="btn btn-default btn-xs py-0" ng-click="MB.resetMachine()">Reset</button>
<button class="btn btn-default btn-xs py-0" ng-disabled="!MB.isRunning()" ng-click="MB.stepMachine()">Step</button>
<button class="btn btn-default btn-xs py-0" ng-disabled="!MB.isRunning() || MB.isPaused()" ng-click="MB.resumeMachine()">Resume</button>
<button class="btn btn-default btn-xs py-0" ng-disabled="!MB.isRunning() || !MB.isPaused()" ng-click="MB.pauseMachine()">Pause</button>

</machine>