During the summer, I had been creating a simple web application in JavaScript. The application contains a small form and a couple of canvas elements to render outputs based on the inputs on the form. I thought this might be a good opportunity to test out Ractive for making the form reactive and maybe some more interesting things I can do. The completed application is hosted on this blog – here.

laplace1

laplace2

I ended up accomplishing two things with this neat framework. I was able to painlessly setup two way binding and structure the app in the MVC pattern with partial templating . This is pretty much what Angular offers, but I have tendency to dislike Angular because of its vast deviation from the traditional JavaScript programming, the involuntary overhead it creates, steep learning curve, and the debugging nightmare. I think using Ractive solved most of these issues. I was able to figure out how it’s supposed to be used in less than 10 minutes, and everything worked like charm with great performance.

Here’s the entire code for setting everything up. I ran it as a window load callback.

$(function(){
  // Load the template file.
  $.get( 'laplace.html' ).then( function ( template ) {
    ractive = new Ractive({
      el: '#laplace',
      template: template,

      // Models
      data: {
        // Boundary Options
        boundaries: [
          {position: 'Left', id: 'left', const_def: 0},
          {position: 'Top', id: 'top', const_def: 0, selected_type: 'sinusoid'},
          {position: 'Right', id: 'right', const_def: 0},
          {position: 'Bottom', id: 'bottom', const_def: 1, selected_type: 'array'}
        ],
        // Solution Options
        x: 30,
        y: 30,
        num_iterations: 50,
        // Animation Options
        no_anim: false,
        anim_interval: 120,
        anim_interval_options: [
          {time: 70, name: 'Fast'},
          {time: 120, name: 'Normal (Recommended)'},
          {time: 240, name: 'Slow'}
        ]
      }
    });
  });
});

laplace.html is the html document that contains all the DOM objects necessary for the app to run. Everything else like doc type declaration, header, and loading library are stored in index.html.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
  <link rel="stylesheet" type="text/css" href="css/style.css?1">
</head>
<body>
  <div id="laplace"></div>
  <script>...</script>
  <script>...</script>
  <script src="js/index.js?1"></script>
</body>

The Ractive initializer will find the element with id #laplace and render the partial “laplace.html” there asynchronously much like how partials are used in any template engines. This is a server-less app, so being able to do this is actually pretty cool. In “laplace.html”, the form element looks like the below.

<form class="form">

        <h3>Boundaries</h3>
        {{#boundaries}}
        <div class="form-group boundary form-inline" id="boundary-{{id}}">
          <label>{{position}}:&nbsp;</label>
          <select class="param-boundary-type form-control" value="{{this.selected_type}}">
            <option value="constant">Constant</option>
            <option value="sinusoid">Sinusoid</option>
            <option value="polynomial">Polynomial</option>
            <option value="array">Array</option>
          </select>
          {{#if this.selected_type == "constant"}}
          <span class="constant">
            <input type="text" class="form-control constant_param1" value={{const_def}}>
          </span>
          {{/if}}
          {{#if this.selected_type == "sinusoid"}}
          <span class="sinusoid">
            <input type="text" class="form-control sinusoid_param1" value='1'>
            <span>sin&nbsp;(</span>
            <input type="text" class="form-control sinusoid_param2" value='2'>
            <span>(PI/8) x) + </span>
            <input type="text" class="form-control sinusoid_param3" value='1'>
          </span>
          {{/if}}
          {{#if this.selected_type == "polynomial"}}
          <span class="polynomial">
            <input type="text" class="form-control polynomial_param1" value='0.05'>
            <span>x^3 + </span>
            <input type="text" class="form-control polynomial_param2" value='0'>
            <span>x^2 + </span>
            <input type="text" class="form-control polynomial_param3" value='0'>
            <span>x</span>
          </span>
          {{/if}}
          {{#if this.selected_type == "array"}}
          <span class="array">
            <input type="text" class="form-control array_param1" placeholder="Comma separated numbers e.g. 1,1,1,2,4,1,1,5..." value='1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1'>
          </span>
          {{/if}}
        </div>
        {{/boundaries}}

        <h3>Solution Options</h3>
        <div class="form-group form-inline">
          <label>Solved Region:</label>
          <input type="text" class="form-control solved-region-x" value={{x}} placeholder="x">
          <span> x </span>
          <input type="text" class="form-control solved-region-y" value={{y}} placeholder="y">
          <span>(capped at 100 each)</span>
        </div>
        <div class="form-group form-inline">
          <label># of Iterations:</label>
          <input type="text" class="form-control num-iterations" value='{{num_iterations}}' placeholder="x">
          <span>(capped at 200)</span>
        </div>

        <h3>Animation Options</h3>
        <div class="form-group">
          <label>
            <input type="checkbox" id="param-solve-immediately" checked={{no_anim}}> Solve Immediately W/O Animation
          </label>
        </div>
        {{#if !no_anim}}
        <div class="form-group form-inline">
          <label>Animation Speed:</label>
          <select class="unit-size form-control" value="{{anim_interval}}" >
            {{#anim_interval_options}}
            <option value={{time}}>{{name}}</option>
            {{/anim_interval_options}}
          </select>
        </div>
        {{/if}}
        <button type="button" class="btn btn-primary" id="solve-button" onClick="solve()">Solve</button>
      </form>

It supports conditional statements and loops, making the form less repetitive. Obviously this isn’t really perfectly normalized code, but without Ractive, it would have been much messier. We can access model via interacting with the ractive object we created

ractive.get('x')

The fact that all this is done with very minimal setup with almost zero learning curve is incredible. I could probably namespace different scopes using different Ractive objects just like directives/scopes in Angular. I know Angular has a lot more features like factories and it enforces the tight MVC structure, which is nice, but I want the freedom of configuring all of those on my own. I like minimal overhead and less “black magic” that seem to happen in a lot of popular frameworks. For this reason, for projects like this, I would again choose Ractive.

Full source code can be found in my Github page – here.