Wednesday, January 1, 2014

Calling JavaScript Libraries from Polymer.dart


I started writing a chapter for Patterns in Polymer and realized that it really wasn't any kind of pattern—at least not for Dart. The latest chapter in the book was to discuss using third party libraries with Polymer. My problem is that Dart is brilliant at package management. Thanks to Dart Pub, using third party libraries is not so much a pattern as it is everyday, humdrum language tools.

So what is a poor author to do? Abandon the pattern because it does not apply to both JavaScript and Dart? That does not seem right since it really does take some thinking in JavaScript. Write a JavaScript-only chapter? Rip-off for the readers of the Dart-only version of the book! Instead, I think that I will give it a go using third party JavaScript libraries from Polymer.dart. Surely there will be a use case for that so it is not completely contrived. Right?

Since my JavaScript example makes use of the debounce() method from the venerable Underscore.js, I think that I will try using the same method from Dart. This is a little contrived because debouncing (aka throttling a function so that it cannot happen again until some time has elapsed) is pretty easy in vanilla Dart. Still, there are some things that JavaScript libraries can do that JavaScript cannot just yet. I think. I mean, there must be, right? Well, assuming there are...

I start with a basic pubspec.yaml for my example “underscore” application:
name: underscore
dependencies:
  polymer: any
A quick pub get and I have all of the Dart dependencies that I am going to need.

I start from the outside of my application with the web page that will hold my Underscore-powered <hello-you> Polymer:
<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- ... -->
    <!-- Load component(s) -->
    <link rel="import" href="/packages/underscore/hello-you.html">
    <!-- Load Polymer -->
    <script type="application/dart">
      export 'package:polymer/init.dart';
    </script>
  </head>
  <body>
    <div class="container">
      <hello-you></hello-you>
    </div>
  </body>
</html>
There is nothing new there—just importing the <hello-you> definition and using it in the page. What is especially nice is that I do not have to declare any JavaScript dependencies or pollute my main page with dart:js-related <script> tags. I can do that entirely in my Polymer definition itself.

Speaking of the Polymer definition, it looks very much like any other <polymer-element> definition, with a <template> describing the HTML used and a <script> tag referencing the actual code. The only thing out of the ordinary is the opening of the file, which includes the dart:js <script> tags and a reference to Underscore:
<script src="../../packages/browser/dart.js"></script>
<script src="../../packages/browser/interop.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js"></script>
<polymer-element name="hello-you">
  <template>
    <h2>Hello {{your_name}}</h2>
    <p>
      <input value="{{your_name}}">
      <input type=submit value="Colorize!" on-click="{{feelingLucky}}">
    </p>
    <!-- ... -->
  </template>
  <script type="application/dart" src="hello_you.dart"></script>
</polymer-element>
Even if the main web page needed to use some JavaScript of its own, this would still work. The main web page could point <script> tags to the same dart:js script tags without worry—modern browsers will retrieve and execute the same script only once.

All that remains is the hello_you.dart backing code. It needs to import the dart:js package in order to have access to the nice JavaScript interoperation methods:
import 'package:polymer/polymer.dart';
import 'dart:math';
import 'dart:js';

@CustomTag('hello-you')
class HelloYou extends PolymerElement {
  HelloYou.created(): super.created();

  var _debouncedLucky;
  feelingLucky() {
    _ensureDebouncedLucky();
    _debouncedLucky.apply([]);
  }

  _ensureDebouncedLucky() {
    if (_debouncedLucky != null) return;
    _debouncedLucky = context['_'].callMethod('debounce', [_feelingLucky, 750, true]);
  }

  _feelingLucky() {
    // Normal Dart definition here...
  }
}
In there, I callMethod() on Underscore to get a “debounced” version of the regular _feelingLucky() method. To invoke this JavaScript debounced method whenever the Polymer invokes feelingLucky() via the on-click attribute, I use dart:js's apply().

And that does the trick:



I can click the “Colorize!” button as often as I like, but will only see a change if I wait for the debounce-mandated 750 milliseconds between clicks. Nice!

As I said, using Underscore from Dart is almost certainly useless. What features of Underscore that are not built into Dart are readily available from packages, including an Underscore Dart package. Still, this is simple enough—and close enough to the JavaScript narrative—that I think it is worth including in the book.


Day #983

1 comment: