AngularDart vs. Coffeescript + AngularJS: How Dart won over Coffeescript halfway through the game

About a month ago, I attended a Dart Flight School with the Philly JavaScript Developers meetup. As someone embroiled in “optimized for developer happiness” languages like Ruby and Coffeescript, I found it easy to make fun of Dart’s superficial ugliness. @NgController declarations lol! Why are they making me use this crappy Eclipse editor? And srsly, what’s with the semicolons?

I wanted to give Dart a fair chance. Victor Savkin, who used to blog about Ruby and make cool projects with it, had started raving about Dart, and it made me curious. Still, writing all of those semicolons and curly brackets made me wonder what the same simple little app would look like in Coffeescript and AngularJS.

Intended audience/About the author

This article is written from the perspective of and thus mostly for developers who:

The Setup

For the Dart Flight School, I completed the ng-darrrt-codelab tutorial. The finished product has a text input field where you can enter your name, and then a name badge where your name shows up as you type. The name badge also includes a pirate-inspired appellation, like “Chad the Wily” or “Chad the Stalwart”. If you don’t feel like writing your own name, there’s also a button that generates a nice, piratey name for you.

For this blog post, I went through the same steps but with Coffeescript and AngularJS instead. The rest of this post is going to be a more fleshed-out version of my commit log.

Step 3: Where things really start to differ

Steps 1 and 2 are uninteresting, so we’re skipping right to Step 3. Here’s the original tutorial, here’s my Coffeescript version.

The differences at this step were all small and are unlikely to convince you to go use Dart. As a complete AngularJS and Dart n00b, though, I still found them interesting.

Observation 1: AngularDart is less picky, kinda

Since I’m new to both AngularJS and Dart, even minor differences tripped me up. The first such minor difference:

You instruct the Angular code which part of your page to manipulate by adding an ng-app attribute to an element. Since I was using Angular on the entire page, I added this attribute to the <html> tag.

In Coffeescript, I needed to be explicit and say which Angular app/module to associate with the <html> element. Even though, in my JavaScript, I only defined one. So, I had to write:

<html ng-app="pirateBadge">

whereas in AngularDart, all I needed was:

<html ng-app>

Observation 2: Surprisingly, Dart reads more clearly than JavaScript sometimes

Dart:

bool get inputIsNotEmpty => !name.trim().isEmpty;

Coffeescript:

$scope.inputIsNotEmpty = -> if $scope.name.match(/\S/) then true else false

Sure, I find the bool get and the semicolon slightly off-putting, but I still think the Dart code expresses its intention more clearly. Hey JavaScript, making me drag in regular expressions just to check if a string is empty? Bummer, dude. You should fix that. I’ll check back in two years to see if all of the important browsers play along with your solution.

(If there’s a smarter way to do this in JavaScript/Coffeescript and I just don’t know about it, tweet at me.)

Update 02014-04-20: A cleaner-reading way to do this in Coffeescript would be:

$scope.inputIsNotEmpty = -> if $scope.name.trim() !== ""

Note that trim (which I didn’t know about before) is not available natively in IE8. That doesn’t bother me much these days, so this critique of Coffeescript is moot. Coffeescript and Dart read equivalently.

Thank you, @EichTyler, for the recommendation.

Observation 3: Ain’t no $scope in Dart

If you’re familiar with AngularJS at all, then you’re good pals with $scope. $scope is how Angular shares data and methods between your controllers and your view without either of them needing to know too much about each other.

In Coffeescript: the controller has a name (BadgesController), and you associate this controller with a certain element in your view by using said name (<body ng-controller="BadgesController">). In the controller, you define attributes and functions on $scope, which you then access directly in the view ({{name}}).

In Dart: the controller declares a selector ([badges]), and you associate this controller with a certain element in your view by using this selector (<body badges>). You define attributes and functions in your controller using its normal public interface, and set a “publish as” namespace, which you must use to access these attributes and functions in your view ({{ctrl.name}}).

So which is prettier? Which is “better”, from a programming perspective? I don’t know. I’m a mediocre judge of the latter, and in terms of aesthetics, they both have pros and cons. I think I slightly prefer the JavaScript approach to things, so I don’t have to see ctrl all over the place.

The Score So Far

These were all minor differences, so I’ll weigh these scores lightly in the final tally. But:

Dart: 2 points 1 point
Coffeescript: 1 point

Step 4: Custom components are super cool, y'all

Step 4 of the Dart tutorial got me excited. AngularDart lets you easily make your own custom component, which you can use all over your html with ease:

<badge name=""></badge>

This uses the Shadow DOM (what the heck is the Shadow DOM? Great question.) and seems like the future to me. These components get their own CSS and Dart code associated with them, and seem like they would make all sorts of development tasks way simpler.

As an AngularJS n00b, it was hard to figure out how to do the same thing in Coffee/JavaScript. I actually thought AngularJS lacked this feature, and so went and learned about PolymerJS, which I’ll talk more about later.

It turns out AngularJS doesn’t (yet) have a separate notion of Components. Instead, Components are just a subset of AngularJS’s larger concept of Directives. A “Component” in AngularJS is a directive with restrict set to "E"—which is perfectly clear, right? ;-)

In addition to finding AngularDart’s syntax for this much more approachable, these are other benefits that it has over AngularJS’s version of components:

Scoping is the worst part of AngularJS’s components

In my main view, I had this:

<badge name="{{name}}"></badge>

And then in my component, I had this:

<span id="badgeName" ng-cloak>{{name}}</span>

This worked, but that’s because both were directly accessing $scope. However, I thought I was passing the name parameter to my component using its name attribute, & that doing so Just Worked™. When I got to the next step and changed my main view to this:

<badge name="{{pirateName()}}"></badge>

The component kept on using the main $scope.name. This meant that my badge said boring ol’ “Chad” instead of something exciting like “Chad the Ultimate”.

The solution is crazy! After some digging, I figured out that I had to add this to the directive definition:

scope: { name: '@' }

Can you guess what it does? …No?

Apparently someone thought this was a clear way to state that the element will have an attribute called name, that you’d like the component’s scope to be separate from the main scope, and that the name attribute passes its value as a string to the child scope. I, on the other hand, have trouble even understanding what I just wrote.

I mean, look at this directive in AngularJS and compare it to the component in AngularDart. AngularDart is just hands-down the winner for code clarity.

(How does AngularDart solve the problem? It sheds the global $scope and instead uses the slightly unsightly ctrl-type namespaces. Hmm… Maybe AngularDart should have gotten all three points in Step 3!)

A Detour Through Polymer

I actually had such a hard time finding AngularJS’s version of components that I thought it didn’t have them when I first worked through this exercise. Instead, I ended up using PolymerJS, which you can see on the master branch of my experiment.

The syntax is a bit different, but Polymer’s implementation of custom elements is closer to AngularDart’s. Both use emerging standards like Shadow DOM and whatnot.

You might be wondering which is better. Polymer was ported to Dart, so should we be using it to make these custom components instead of Angular’s ish? Good question! From what I gathered, here’s what the difference seems to be:

The Polymer team is reimagining the web and casting vision for how these new standards ought to work. As the standards become implemented in browsers, Angular will use them under the hood.

I imagine I’ll mostly use Angular’s components. However, I might still use Polymer for certain things. Polymer enables building components that work in many projects, instead of one particular Angular app. So if I want a custom date picker, or a 3D div-cube, or any general-purpose UI thing, Polymer’s the better choice.

The Score So Far

This one I felt more strongly about than Step 3, so I’m going to weight it at a 5. AngularDart gets 1 point (weighted at 5), and AngularJS gets 0. Total:

Dart: 7 points 6 points
Coffeescript: 1 point

Step 5: Aaaand I’m ready to call Dart the winner

In this step, the tutorial had me add a model in the form of a plain Dart/JS class. As I mimicked the steps with AngularJS, here are some things that jumped out at me:

These were all small things. Things that I’d be willing to overlook for Coffeescript’s syntactic beauty. But then, the clincher:

Know what sucks? Languages that gloss over your mistakes

In Dart:

class PirateName {
  String firstName, appellation;
  PirateName([this.firstName = '', this.appellation = '']);
}

In Coffeescript:

class PirateName
  constructor: ->
    @firstName = ''
    @appellation = ''

Here’s a story about why Dart’s version wins this whole competition:

When I was writing this step in Coffeescript, I was accidentally trying to set a lastName attribute on my PirateName object. JavaScript just partied on with this, which meant I did not get an error. Instead, it just changed my PirateName object to

{ firstName: 'Jane', lastName: 'Noble', appellation: '' }

Since I was then only displaying the firstName and appellation attributes of this object, I ended up with “Jane the ” in my view, instead of “Jane the Noble”. In this case, tracking down the problem only took one console.log statement. During real app development, tracking down such problems can waste hours.

In Dart, if had I made a similar mistake, I would have gotten a helpful stack trace telling me exactly what I did wrong:

Class 'PirateName' has no instance setter 'lastName='.

NoSuchMethodError : method not found: 'lastName='
Receiver: Instance of 'PirateName'
Arguments: ["Noble"]

STACKTRACE:
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:45)
#1      BadgesController.name= (http://127.0.0.1:3030/piratebadge.dart:44:9)
#2      ...

I like flexibility as a concept, and I think there are ways that languages like Ruby can do it well. But dang, I love that Dart fails fast here. I don’t want a language that silently glosses over my errors!

Conclusion: I wanna go build things with Dart now

I wish Dart were syntactically pretty. I wish it looked more like Coffeescript or Ruby, instead of Java or whatever icky thing it looks like.

BUT. I think they made great structural design decisions. I think the way the language actually works seems much more conducive to developer happiness than cruddy JavaScript. And let’s not kid ourselves, Coffeescript is just a thin veneer over the roiling sea of flesh-eating parasites that is JavaScript. A gorgeous veneer! But you’re still likely to wind up with less flesh on your feet.

The better structural design decisions carried over into Dart’s port of Angular. From my limited experience, I think learning Angular seems easier with the Dart version than with the JavaScript version.

Since Dart has such powerful tools as Angular and Polymer, I suspect I’ll be able to build some impressive apps without feeling sad that I don’t have access to jQuery or any of the other 65,000 JavaScript libraries available on npm. And if I need those things, there is a JavaScript interop for Dart, though I’ve gotten bad vibes about that.

Of course, this investigation didn’t even get into how much more performant a typical web app might be with dart2js instead of my shoddily built Coffeescript-2-JS apps. I’m a Rubyist, you know? I generally worry more about tuning for developer happiness than I do about performance. But with Dart, I think I’ll be better off in both categories.


See any mistakes? Have comments? Think I seem rad? @chadoh or hi@chadoh.com

 
436
Kudos
 
436
Kudos

Read this next

Episode -2: 0xE2? Wtf? Addendum to the MySQL series

Hello, human. You probably learned to count to ten. You probably use 0 to 9 to write numbers. That’s great! But there are other ways to do things. Computers, you’ve probably heard, store everything as ones and zeros.... Continue →