Code Retreat and Angular

Mon Dec 10, 2012

Before I get to the actual article, there was enough interest in the sparse-array solution to Life this past Saturday that I've added them to the appropriate Rosetta Code page and my GitHub. Sadly, the only solutions up are python, common lisp, haskell and clojure. The Prolog attempt my partner and I made during the event wasn't completed, and I never actually got to try Forth. I might get around to doing some thinking with those; I'll keep you posted.

Global Day of Code Retreat

Was pretty goddamn awesome. For some reason, I became known as "the Haskell guy", despite the fact that I am nowhere even approaching mastery|1|. The attempts I made were

  1. Prolog - With someone who is older than me and hadn't done any Prolog since graduating. This wasn't helped by the fact that the entirety of my Prolog experience was two read-throughs of the appropriate chapter in Seven Languages. We got as far as figuring out how to compute a Moore neighborhood before time was called.
  2. Common Lisp - With a young man looking to learn the language. Hopefully, I wasn't too enthusiastic in recommending that he drop by the |2|, and gave him enough of a taste for Lisp that it won't look alien next time. Unfortunately, the problem we were solving ran smack into Common Lisps' poor support for Web Mote and Angular.js

    You haven't heard much about Web-Mote lately, and that's mainly because I've been porting it to Angular.js, saving myself quite a few lines of code in the process. You may remember that I called all the JS-MVC frameworks shit a little while ago. While my distaste for the needless OO-modeling that permeates most of them remains intact, a fellow Lisping web-developer told me to give Angular another try without reading their godawful, over-engineered tutorial.

    I've almost finished porting, and I must admit that I've become a believer. If you're already using

    you probably could have saved yourself a lot of code and headache by just picking up Angular, which elegantly solves most problems you'd be using the above for. It also turns out that |3|.

    The "templating" in particular deserves special mention, though that's probably the wrong thought model to apply here. As the fellow Lisper explained, Angular gives you a DSL for writing HTML front-ends. To illustrate, here's what the HTML component of the Handlebars/Backbone/jQuery version looked like

    <!DOCTYPE HTML>
    <html lang="en-US">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width" />
        <title>WebMote</title>
      </head>
      <body>
    
        <!-- --------- -->
        <!-- Templates -->
        <!-- --------- -->
        <script id="tmp-folder" type="text/x-handlebars-template">
          <li class="{{type}}">
            {{#if buttons}}
            <button class="play btn" onclick="mote.play('{{path}}')"><i class="icon-play"></i></button>
            <button class="shuffle btn" onclick="mote.shuffle('{{path}}')"><i class="icon-random"></i></button>
            {{/if}}
            <a class="dir-link{{#unless buttons}} buttonless{{/unless}}" href="#navigate{{path}}">{{name}}</a>
          </li>
        </script>
    
        <script id="tmp-file" type="text/x-handlebars-template">
          <li class="{{type}}">
            <a class="file-link" href="javascript:void(0);" onclick="mote.play('{{path}}')">{{name}}</a>
          </li>
        </script>
    
        <script id="tmp-control" type="text/x-handlebars-template">
          <li class="{{cmd}}">
            <button class="btn" onclick="mote.command('{{cmd}}');"
                    {{#if held}}
                    onmousedown="mote.hold('{{cmd}}');" onmouseup="mote.release();" onmouseout="mote.release();"
                    {{/if}}>
              <i class="icon-{{cmd}}"></i>
            </button>
          </li>
        </script>
    
        <script id="tmp-control-block" type="text/x-handlebars-template">
          <ul>
            {{#each this}}
            {{#control-button this}}{{/control-button}}
            {{/each}}
          </ul>
        </script>
    
        <!-- ---- -->
        <!-- Body -->
        <!-- ---- -->
        <ul id="file-list"></ul>
        <div id="controls"></div>
    
        <!-- ------ -->
        <!-- Styles -->
        <!-- ------ -->
        <link rel="stylesheet" href="css/custom-theme/jquery-ui-1.8.13.custom.css" type="text/css" media="screen" />
        <link rel="stylesheet" href="css/bootstrap.css" type="text/css" media="screen" />
        <link rel="stylesheet" href="css/bootstrap-responsive.css" type="text/css" media="screen" />
        <link rel="stylesheet" href="css/style.css" type="text/css" media="screen" />
    
        <!-- ------- -->
        <!-- Scripts -->
        <!-- ------- -->
        <script type="text/javascript" src="js/jquery.min.js"></script>
        <script type="text/javascript" src="js/jquery-ui-1.8.13.custom.min.js"></script>
        <script type="text/javascript" src="js/handlebars-1.0.rc.1.js"></script>
        <script type="text/javascript" src="js/underscore-min.js"></script>
        <script type="text/javascript" src="js/backbone-min.js"></script>
    
        <script type="text/javascript" src="js/web-mote.js"></script>
    
      </body>
    </html>
    

    and this is what the exact same front-end looks like expressed in the Angular HTMLDSL

    <!DOCTYPE HTML>
    <html lang="en-US">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width" />
        <title>WebMote</title>
      </head>
      <body>
    
        <div ng-app>
          <ul id="file-list" ng-controller="FileListCtrl">
            <li class="{{file.type}}" ng-repeat="file in filesList" ng-switch="file.buttons">
              <span ng-switch-when="true">
                <button class="play btn" ng-click="play(file.path)"><i class="icon-play"></i></button>
                <button class="shuffle btn" ng-click="shuffle(file.path)"><i class="icon-random"></i></button>
                <a class="dir-link" ng-click="play(file.path)">{{file.name}}</a>
              </span>
              <a class="dir-link buttonless" ng-switch-default ng-click="play(file.path)">{{file.name}}</a>
            </li>
          </ul>
          <div id="controls" ng-controller="CommandCtrl">
            <ul ng-repeat="controlsList in controlTree">
              <li ng-repeat="control in controlsList" class="{{control.cmd}}" ng-switch="control.held">
                <button class="btn" ng-switch-when="true"
                        ng-mousedown="command(control.cmd); hold(control.cmd)"
                        ng-mouseup="release()" ng-mouseleave="release()">
                  <i class="icon-{{control.cmd}}"></i>
                </button>
                <button class="btn" ng-switch-default ng-click="command(control.cmd)">
                  <i class="icon-{{control.cmd}}"></i>
                </button>
              </li>
            </ul>
          </div>
        </div>
    
        <!-- ------ -->
        <!-- Styles -->
        <!-- ------ -->
        <link rel="stylesheet" href="css/bootstrap.css" type="text/css" media="screen" />
        <link rel="stylesheet" href="css/bootstrap-responsive.css" type="text/css" media="screen" />
        <link rel="stylesheet" href="css/style.css" type="text/css" media="screen" />
    
        <!-- ------- -->
        <!-- Scripts -->
        <!-- ------- -->
        <script type="text/javascript" src="js/jquery.min.js"></script>
        <script type="text/javascript" src="js/angular.min.js"></script>
        <script type="text/javascript" src="js/angular-resource.min.js"></script>
    
        <script type="text/javascript" src="js/mote.js"></script>
    
      </body>
    </html>
    

    What you don't see above is that the amount of JavaScript required for this new approach is easily 1/2 of what I needed to write to get comparable functionality with separate templating/routing/DOM libraries. Most of it, I get the feeling, is Angulars' use of reactive programming, but I can't really be sure of that. Since I haven't done much testing yet, I also don't know what kind of performance hit I'm going to be taking by using such a transformative approach.

    Add that to the massive list of things I need to keep you posted about, I guess.


    Footnotes

    1 - |back| - But give me ten years or so.

    2 - |back| - Where we actually discuss many things of interest to functional programmers, not just Common Lisp.

    3 - |back| - So you can still use jQuery for the one or two things Angular doesn't bother with.


Creative Commons License

all articles at langnostic are licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License

Reprint, rehost and distribute freely (even for profit), but attribute the work and allow your readers the same freedoms. Here's a license widget you can use.

The menu background image is Jewel Wash, taken from Dan Zen's flickr stream and released under a CC-BY license