Wednesday, July 31, 2013

Developing VectorShapes.com with Dart



VectorShapes has been coded from the ground up with the new Dart programming language. Since the dartlang movement is relatively new I thought I'd share my experience here...

Why Dart?

You might guess by the fact that I've labelled myself as a Dart Developer my experiences have been positive. Dart, for me, is all that I like about coding with GWT, jQuery, ASP.NET but without the drawbacks.

If I were to choose one reason as to why I now code in Dart, I would have to say that it is the integrated development experience provided by the language and the IDE. Breaking this down:
  • the structured nature of the language; 
  • the option to declare types and the way that you can use types to orientate yourself in the code; 
  • front-end dart is modeled on/supports the standard DOM APIs (HTML and SVG);
  • the code is decoupled from the markup (with bindings via pub libs if required); and 
  • the immediacy and responsiveness of the dart editor.
I'm currently using Dart on the front end only. While IMAO I can see potential from server-side,  for me the development experience it is not quite there yet  (I did try!).

Some code gems

Following are a few snippets of Dart that I found useful in the project:

1. Working with collections of Elements.

I've grown fond of the forEach statement coupled with an explictly typed callback parameter. With the code completion feature of the editor it makes following your line of thought a breeze.

  QueryAll('input[type=checkbox]').forEach((InputElement e){
      print(e.checked);
  });
  Map<String,int> myMap = {"item1":1,"item2":2};
  myMap.forEach((String key, int val)=>print("key: $key value: $val"));

2. Futures and Completers

This elegant and common sense approach to asynchronous programming is great.

  Completer completer = new Completer(); //pass me into a method
  Completer.complete.then((val) => doSomething(val));
  myMethod(completer);

  void myMethod(Completer completer){
    ...
    completer.complete("green eggs");
  }

  void doSomething(val){
    print("Do you like $val?");
  }


4. Relative Mouse Movement

Calculating this manually isn't rocket science, but anything that lets the coder focus on the problem at hand is welcome.

  myElement.onMouseMove.listen((MouseEvents e){
    print("X:${e.movement.x} Y:${e.movement.y}");//eg. X:4 Y:-2  
  });

5. With syntax and attributes map

  myElement.sytle..width = "10px"
                 ..height = "10px"
                 ..opacity = "0.5";

  SvgElement path = new SvgElement.tag("path");
  path.attributes = {
    "d":pathData,
    "fill":fill,
    "fill-opacity": fillOpacity,
    "stroke":stroke,
    "stroke-width":strokeWidth,
    "opacity":opacity,
    "stroke-opacity":strokeOpacity
  }; 

6. Strings

I was shocked when the Dart team took the '+' string concatenation operator out of the spec, but I've found that the language more than makes up for this with string interpolation, multi-line strings and other features of the String class.

String msg = """
  This was submitted to <a href='${myLinkUrl}'>The 5k Awards</a> in 2002.
  Then you needed a plugin, today it works natively in most browsers and AI.
  """;

Edit: Thanks to Yissachar Radcliffe for pointing out that the '+' operator is now back in the language. This'll be useful for porting js to Dart as removing '+' from a lot of code can be painful!

7. Optional Params

The following compensates for lack of method overloading.

  myMethodA([num val]) => print(val);
  myMethodB({Element e, String c, dynamic t})=> print(" $e $c $t") ; 

  main {
    myMethodA();  //null
    myMethodA(5); //5
    myMethodB();  //null null null
    myMethodB(t:"foobar"); //null null foobar 
    myMethodB(t:"foobar",e:new SvgElement.tag("rect")); //rect null foobar
  }

App Structure

Note: not sure if this is the best way to go, so any feedback appreciated

I've taken the approach of breaking the VectorShapes solution into:
  1. a main dart app
  2. many stand alone sub-apps that are included in the main app via content windows (iframes)
  3. my custom src libraries (local packages).

The following screenshot shows the projects in the solution workspace.


To include a sub-app in the main html file:

<iframe id="frameTiltshift" class="contentWindow" src="../../vs_tiltshift/web/vs_tiltshift.html" ...></iframe>

Right now there are only a few apps on the site so I'm just loading them all when the visitor hits the main page, but in the future it will be easy to leave out the src attribute and add it later to load the page when needed.

An advantage I've found of having each app page in the site run from it's own entry point is that you can run and debug each app independently.

Messaging between contentWindows.

The days of parent.window.doSomething are gone and now we must post messages between our content windows. This is a good thing because it tightens up communication between the windows and is therefore easier to find, read and maintain these operations.

For example. Any message received by the content window must hit the routeMessage() method (including a messages sent to the window from it's native JavaScript code). Simply...

  window.onMessage.listen((e)=> routeMessage(e.data));

  ...

  void routeMessage(String data){
    List a = data.split('|');
    String op = a[0];
    String payload = a.length>1?a[1]:null;
    switch(op){
      case "COPY_TEXT": showCopyText(payload); break;
      case "MSG_ERROR": showMsg("error",payload); break;
      ...
    }
  }

And to post a message to the main window from inside the sub-apps...


  window.parent.postMessage("COPY_TEXT|${query("#mainDisplay").outerHtml}", "*");


When a user subscriber signs in to the site they have access to reserved content. Broadcasting a message to all contentWindows on a sign in event is as simple as...

  queryAll(".contentWindow").forEach((IFrameElement e){
    e.contentWindow.postMessage("MY_OPERATION", "*");
  });

Notice the 'wildcard' argument on the postMessage method. The projects will be deployed with a targetOrigin of "http://vectorshapes.com". As there are many messagePosts in this app, this is a real pain to update on deployment. This makes me think that a deployment macro feature would be very useful -- In my code I would write something like .postMessage("MY_OPERATION", #myTargetOrigin) and then in the editor config have a debug-deployment macro setting that would contain a set of named key value  pairs with myTargetOrigin = debug: "*"; deploy: "http://vectorshapes.com"...just an idea!

Another feature request will be to choose a deploy folder location for each project.

pub.dartlang.org

You can do a lot without having to go beyond the 'built-in' dart packages (dart:html, dart:async, dart:svg, etc). But if you need to go into a specific code task then there is a range of libraries available at pub.dartlang.org. I am a big fan of both the vector_math and the box2d packages which were used in the project.

Google+ Sign-In and Google Wallet Checkout 

These features were included in the site as native JavaScript. I look forward to the time when Google provides these as Dart packages on pub.dartlarg.org as I'll be able to remove all js from the project.

Summary

In summary, I believe Dart has arrived as a development solution just at the right time for developers to take full advantage of the HTML5 landscape.  The Dart language and Dart Editor have made the VectorShapes development experience easier and more fun.



1 comment: