Wednesday, February 5, 2014

Angular and Polymer Data Binding, Together!





Angular and Polymer, sitting in a DOM tree,
B-i-n-d-i-n-g.
First comes components,
Then comes elements,
Then comes the interop with the node dot bind.

Angular, a super heroic MVC framework, and Polymer, polyfills and enhancements for custom elements built on top of Web Components, can live harmoniously in the same app. This post shows you how to connect Angular-controlled components to Polymer-controlled elements via data binding. And we do it all in Dart.

Angular and Polymer


I get asked "Should I use Angular or Polymer?" a lot. My answer is, "Yes". That is, both libraries have distinct strengths, and you can use both in the same app.

Polymer excels at creating encapsulated custom elements. You can use those custom elements in any web app or web page, regardless if that app is built with Angular, Ember, etc. Angular excels at application engineering, with dependency injection, end-to-end testability, routing, and services.




Here are some features that Angular offers:
  • directives - kind of like mixins or traits for DOM nodes
  • dependency injection
  • testing support
  • routing
  • services
  • server communication
Here are some features that Polymer offers:
  • <polymer-element> - convenient way to declare custom elements
  • widgets
  • polyfills for new web platform features
  • encapsulation for the DOM
  • built on emerging web standards
There are some similar features between the two libraries. Both libraries implement a data binding solution (which we cover below). Angular is beginning to build on top of emerging web standards (like Shadow DOM). I expect that, over time, more and more of Angular will take advantage of the Web Components family of specifications.

Interop

We can interop Angular and Polymer via the DOM, as well as via data binding.

DOM interop


We've always been able to use the DOM for basic interop between custom elements and Angular. For example, setting an attribute on a custom element is a type of API interop. Simply creating the custom element and inserting it into the document is another type of interop. Polymer and Angular already interop at the DOM layer.

For example, here's an example of an Angular managed page that includes a Polymer element.



In the above code, Angular manages the binding of the cool variable in the HTML. When cool changes, everywhere you see {{cool}} also changes. Yes, even when bound into an attribute of a custom element!

The <my-element> is just a custom Polymer element encapsulates an input field and displays a value. Here's the code:


<polymer-element name="my-element">
  <template>
    <p>
      <input type="text" value="{{message}}"> Polymer binding: {{message}}
    </p>
  </template>
  <script type="application/dart" src="my_element.dart"></script>
</polymer-element>

The Angular code can set attributes on <my-element>, listen for DOM events from <my-element>, and of course even insert children elements inside of <my-element>. So in a sense, Angular can interop with Polymer elements just like any other DOM element.

However, there is an issue. Can you spot it in the animated GIF below?


Any changes to cool initiated from inside the Polymer element are not propagated back out into the outside DOM or Angular. We need real bi-directional data binding: Angular down into Polymer, and Polymer back out to Angular.

Data binding interop


Polymer manages data binding through a feature called Node.bind(). Node.bind() is a "new method added to all DOM nodes which instructs them to bind the named property to the data provided". Angular does not use Node.bind() out of the box, so Angular is unable to listen for changes that are initiated from Polymer.

Until now.

Justin Fagnani, an engineer on the Dart team, released angular_node_bind, a Dart package that bridges the Angular and Polymer worlds. It is an Angular module that can listen for Node.bind() changes and propagate the changes into Angular.

Justin explains why this package can help:

  • Node.bind() takes care of setting up the binding, including two-way bindings, eliminating the need for directives for every property for two-way bindings.
  • Custom elements that expose properties will be properly bound, again including two-way bindings. You can use the growing collection of custom element libraries in your Angular app.

To use angular_node_bind, add the following to your app's pubspec.yaml file:


name: angularpolymer
description: A demo of Angular and Polymer data binding.
dependencies:
  angular: any
  angular_node_bind: any
  browser: any
  polymer: any
transformers:
- polymer:
    entry_points: web/angularpolymer.html

Register the NodeBindModule with Angular:


import 'package:polymer/polymer.dart' show initPolymer;
import 'package:angular/angular.dart' show ngBootstrap;
import 'package:angular_node_bind/angular_node_bind.dart' show NodeBindModule;

void main() {
  initPolymer().run(() {
    ngBootstrap(module: new NodeBindModule());
  });
}

Finally, change the syntax used for binding into Polymer elements, from {{...}} to [[...]]:


<my-element message="[[cool]]"></my-element>

Notice the binding expression is now [[cool]] instead of {{cool}}. The angular_node_bind module looks for [[ ... ]] and wires up Node.bind().

Here is a little movie of how the app works with true bi-directional data binding between Polymer and Angular:



Notice how changes from Angular are reflected in the Polymer element, and how changes from within the Polymer element are reflected in the Angular code. Awesome!

Summary

You can use Polymer custom elements inside of an Angular app in at least two ways. Polymer elements are just like regular DOM elements, so they have attributes, emit events, and can contain children elements. Use Dart's angular_node_bind package to connect Polymer's data binding to Angular's data binding for true bi-directional data sharing.

Lovely, Angular and Polymer holding hands.

Get the source code

You can check out the source code mentioned in this post.

Wednesday, January 8, 2014

How to shrink the size of your Dart app when compiled to JavaScript

So you caught the Dart bug and you're enjoying using the structured language, comprehensive libraries, and productive tools. Great! Except, when you compiled your Dart app to JavaScript, the output was bigger than expected. Read this post to learn how to reduce the size of your JavaScript output. Small apps are fast apps!

Make sure you are minifying


The dart2js compiler can minify your JavaScript. However, minification is not the default. Make sure you opt-into minification before you deploy to production.

How can you tell if you minified? Take a look at the generated JavaScript and you'll know. Minified JavaScript replaces variables names, function names, and more with shorter names. It also moves code around to use less lines.

If you compile your app from the command line, use --minify:

dart2js --minify -o=app.dart.js app.dart

If you use pub build (a streamlined build process for pub package and apps), it defaults to minifying. Yay!

If you use Dart Editor, it's a bit more tricky. Follow the instructions I posted on StackOverflow for screenshots and a walk through. You need to add --minify to the Run config in Manage Launches.

You will see a significant reduction in size after you minify.

Make sure all dart:mirrors imports use @MirrorsUsed


Dart's reflection capabilities come from the mirrors library. Mirrors are really powerful, but they introduce challenges to dart2js's tree shaking abilities. Normally, dart2js can look at the entire app and deduce what code is required to run the app. However, as of the time of this writing, mirrors (due to their API design and highly dynamic nature) more-or-less disable dart2js's tree shaking.

Enter @MirrorsUsed, a metadata annotation, which explicitly states what parts of the program are reflected. Tools like dart2js can use @MirrorsUsed to re-enable tree shaking.

Note: by the time you read this article, @MirrorsUsed might be deprecated or removed. The engineers are working on better ways to deduce how mirrors are used in the code. As of the time of this writing, @MirrorsUsed is the tool we have to gain control over the output size when mirrors are used.

If your app imports dart:mirrors and you compile it with dart2js, you might see a warning like this:

sethladd:~/dart/test/web$ dart2js test.dart 
test.dart:1:1: Hint: 6379 methods retained for use by dart:mirrors out of 7873 total methods (81%).
import 'dart:html';

mylibrary.dart:3:1: Info: This import is not annotated with @MirrorsUsed, which may lead to unnecessarily large generated code.
Try adding '@MirrorsUsed(...)' as described at https://goo.gl/Akrrog.
import 'dart:mirrors';

^^^^^^^^^^^^^^^^^^^^^^

If you see "This import is not annotated with @MirrorsUsed", then the generated JavaScript is probably too big. My sample app, which uses UUID, HTML, and mirrors, compiles out to > 4MB. Yikes. But we can fix it!

At the import of dart:mirrors, add a @MirrorsUsed and specify what the app reflects on. You can specify:
  • targets: one or more names of libraries or classes
  • metaTargets: one or more classes used as metadata to indicate reflectability
For example:


library mylib;

@MirrorsUsed(targets: 'mylib')
import 'dart:mirrors';

All names and things (classes, functions, etc) inside of mylib will be retained for reflection.

From what I can tell, once dart2js sees a @MirrorsUsed, it then tree shakes and drops all targets that are not specified. So be sure to include a complete list of targets (libraries, classes, etc) that will be reflected.

In my test app (uuid, html, mirrors), the generated JavaScript size dropped to 400k unminified (from 4MB unminified) once I added @MirrorsUsed.

If the import of dart:mirrors is not under your control (e.g. you use a package that imports a package that uses dart:mirrors), you can introduce your own @MirrorsUsed annotation to override. Just introduce your own import of dart:mirrors in code that you control (e.g. in your entry point file), add @MirrorsUsed, specify targets and metaTargets, and set override: '*'.

As mentioned above, Dart engineers are working on making mirrors + reflection + dart2js much friendlier. We know @MirrorsUsed is hard to get right, but it's the only tool we have right now at the time of this writing.

Make sure you are gzipping


Of course, your web server should perform real-time compression. Be sure to account for gzip when calculating how many bits are traveling across the wire.

Summary


To calculate the real size over the wire, minify your app, add @MirrorsUsed if you use mirrors, and enable HTTP compression like Gzip. If you have any questions, please ask on StackOverflow or file a bug at dartbug.com/new

Wednesday, December 11, 2013

Compile-time dead code elimination with dart2js

Right before the release of Dart 1.0, the Dart team snuck in a cool new feature that helps you optimize the generated JavaScript from your Dart apps. Configuration values from the environment can be evaluated at compile time and potentially result in dead code elimination. Smaller code is faster code!

As an example, let's consider the common case of logging. Debug logging is a useful tool during development and testing, but often there's no need to ship production code with debug log statements. Wouldn't it be cool if you could remove all the debug log statements before you ship to production, without having to manually delete any lines of code? Now with Dart and fromEnvironment, you can!

Here is a small example of a log function that is useful during development, but unnecessary during production. If a DEBUG is set, the log message is printed. The code gets the compile-time constant value from the String.fromEnvironment() constructor. Notice the const qualifier, instead of new, as the important signal that the environment value is a compile-time constant. Without const here, the compiler doesn't know what the value is during compilation.

log(String msg) {
  if (const String.fromEnvironment('DEBUG') != null) {
    print('debug: $msg');
  }
}

main() {
  log('In production, I do not exist');
}

For dart2js, you can pass values from the environment (not to be confused with "environment variables" that you might set in your shell) via the -D command line arguments.

When compiled with dart2js -DDEBUG=true app.dart, the output includes the logging behavior:

  main: function() {
    H.printString("debug: In the production release, I do not exist");
  }

Notice how dart2js inlined the log function into main, which is cool. Because the DEBUG value was set, dart2js knew that the if statement inside of log() returns true.

However, if -DDEBUG is not set, the logging behavior is not included in the generated JavaScript code:

  main: function() {
  }

Tools like dart2js can understand how expressions that use compile-time constants resolve. Dead code is eliminated from the generated output.

Strings, ints, and booleans can produce values from the environment: int.fromEnvironment, String.fromEnvironment, and bool.fromEnvironment. Use fromEnvironment with const to help control which code blocks make it into the generated JavaScript code.

Saturday, September 28, 2013

Forms, HTTP servers, and Polymer with Dart

(Update of this old Web UI post. Updated as of Jan 17, 2014 and Dart 1.1.)

Dart can be used on the client and the server. This post shows how to:

  • build a form as a custom element
  • bind input fields to a Dart object
  • build a Dart HTTP server
  • handle and parse a form submit


For lots more Polymer.dart examples, be sure to check out the Dart Polymer Samples. You can find the code for this post at my Github account.

Step 1: Install the packages

Open up pubspec.yaml (used by the pub package manager) and add the dependencies for Polymer.dart and http_server.

name: parse_form_submit
description: A sample Polymer application
dependencies:
  polymer: any
  http_server: any

Step 2: Create the model class

This class is for the "business object". It is bound to the form, so we make its fields observable.

library models;

import 'package:polymer/polymer.dart';

class Person extends Object with Observable {
  @observable String firstName;
  @observable String lastName;
  @observable int age = 0;
}

Step 3: Create the custom element HTML


This custom element, brought to you by Polymer.dart, wraps the form and makes it easy to reuse. The <template> tag contains the structure of this custom element. The submit event runs the doSubmit() method of this custom component (see in the next step).

A Dart class, linked by the <script> tag, backs this custom element.

<!DOCTYPE html>

<polymer-element name="person-form" attributes="action">
  <template>
    <div id="message"></div>
    <form method="post" on-submit="{{doSubmit}}">
      <div>
        First name: <input type="text" value="{{person.firstName}}" name="firstName">
      </div>
      <div>
        Last name: <input type="text" value="{{person.lastName}}" name="lastName">
      </div>
      <div>
        Age: <input type="number" value="{{person.age | asInteger}}" name="age">
      </div>
      <div>
        <input type="submit">
      </div>
    </form>
  </template>
  <script type="application/dart" src="person_form.dart"></script>
</polymer-element>


Step 4: Create the custom element Dart code

If a custom element needs custom behavior, you can implement it with Dart. Each custom element tag you use on the page has an instance of its corresponding class. Here, the PersonForm class goes with the <person-form> tag.

The doSubmit() method is called when the form is submitted (see the on-submit binding from Step 3).

In this case, we disable the default behavior of form submission so we don't incur a page reload. We use HttpRequest to submit the form data, and update a div inside our custom element with the response text.

import 'package:polymer/polymer.dart';
import 'models.dart';
import 'dart:html';
import 'ui_filters.dart' show StringToInt;
import 'package:polymer_expressions/filter.dart' show Transformer;

@CustomTag('person-form')
class PersonForm extends PolymerElement {
  final Person person = new Person();

  PersonForm.created() : super.created();

  @published String action;

  doSubmit(Event e, var detail, Node target) {
    e.preventDefault();

    FormElement form = target as FormElement;
    form.action = action;

    HttpRequest.request(action,
          method: form.method,
          sendData: new FormData(form))
        .then((HttpRequest req) {
          shadowRoot.querySelector('#message').text = req.responseText;
        })
        .catchError((e) => print(e));
  }

  // Filters and transformers can be referenced as fields.
  final Transformer asInteger = new StringToInt();
}

Step 5: Import and use the custom element

Use a <link> tag to import the custom element. You must also export polymer/init.dart, which helps to initialize the app.

<!DOCTYPE html>

<html>
  <head>
    <meta charset="utf-8">
    <title>Sample app</title>
    <link rel="stylesheet" href="parse_form_submit.css">
    <link rel="import" href="person_form.html">
    <script type="application/dart">export 'package:polymer/init.dart';</script>
    <script src="packages/browser/dart.js"></script>
  </head>
  <body>
    <h1>Forms</h1>

    <person-form action="http://localhost:8888/submit"></person-form>

  </body>
</html>


Step 6: Write the server

Dart's dart:io library help you write command-line and server-side application. We use the http_server package for additional HTTP server functionality. The server listens for POST requests, parses the form data into a Map, enable CORS headers (so pages from any origin can submit to this server), and sends back a string version of the original form data.

import 'dart:io';
import 'package:http_server/http_server.dart';

echo(HttpRequest req) {
  print('received submit');
  HttpBodyHandler.processRequest(req).then((HttpBody body) {
    print(body.body.runtimeType); // Map
    req.response
      ..headers.add('Access-Control-Allow-Origin', '*')
      ..headers.add('Content-Type', 'text/plain')
      ..statusCode = 201
      ..write(body.body.toString())
      ..close();
  });
}

main() {
  HttpServer.bind('0.0.0.0', 8888).then((HttpServer server) {
    print('Server is running');
    server.listen((HttpRequest req) {
      if (req.uri.path == '/submit' && req.method == 'POST') {
        echo(req);
      }
    });
  });
}


Summary

Ta da! You've just used Dart to create a live, data-bound form and wrapped it up in a web component. Then, you wrote a Dart server to parse the form data and return a simple response. Be sure to checkout the Github repo for this post for all the code. Is this the best we can do? Probably not, you probably want a more full-featured server-side framework. There are a few in pub.dartlang.org for you to explore.

Learning more

To learn more about Dart, try these resources:

Sunday, September 15, 2013

JavaZone Report. Spoiler: Awesome.

I had the pleasure of presenting Dart and Web Components at JavaZone 2013 in Oslo, Norway, and I'm so very happy I had the chance. The audience was clearly interested in Dart, the organizers are truly professional and welcoming, the crowd was friendly, the A/V setup was top-notch, and the logistics were easy.

JavaZone is produced by JavaBin, a large network of Java user groups across Norway. I believe this was the 12th or 13th year for JavaZone. The conference has the feel of a big happy meetup. It's chill, mostly local attendees, mostly local vendors (though I did see JetBrains and Atlassian), and approximately 2000 attendees. Don't let "meetup" fool you, this is a full conference: two days, seven concurrent tracks, professional A/V, swag, food, etc. I liked how this was a conference built by the fans, for the fans.

There is continuous food during the conference. You will never go hungry during JavaZone. There was also a coffee bar serving individual drop coffees, sponsored by a local consultancy.

There are seven concurrent tracks, and it's sometimes hard to decide which session to attend. Fear not, because JavaZone thought about that, too. In their overflow room, there is a giant screen showing all seven presentations at once! Pick up a headset and select the right channel for audio.



JavaZone is known for Triple-A promo videos, a great party, and a once-in-a-lifetime speaker adventure (more on that later). The organizers have a "full spectrum" (my term, not theirs) approach to creating the event.

You may have seen this promo video for JavaZone 2013. It has the same quality as a Hollywood trailer. If you're a programmer of any sort, you'll probably find this funny.



The JavaZone party, appropriately named AweZone, is held in a music hall. Multiple levels, multiple bands, and free for attendees. Cool fact: many band members are developers from local tech companies. They know how to rock.

Traveling to JavaZone was easy: fly into OSL Oslo airport, get the airport express train straight to downtown Oslo. It takes about 20 minutes. I stayed at the Radisson Blu Park Plaza, which is across the street from the train station and next door to the event venue: Oslo Spectrum.

I was lucky enough to present in the biggest room, with approximately 300 developers there to watch the talk. It's an updated presentation that Justin Fagnani and I did at Google I/O 2013, with more emphasis on the latest web components and polymer.dart work.

If you missed it, here's the video from my talk:


Dart and Web Components - Scalable, Structured Web Apps from JavaZone on Vimeo.

The audience was shy asking questions in front of everyone, but I had a solid crowd of developers come up to me after the talk. Many were interested in what the Angular + Dart story is. Luckily, there's an Angular port for Dart in the works, and the Polymer work is putting many of the concepts from Angular directly into the browser.

With JavaZone such a good conference, is there anything I would changed? I was asked by one of the organizers, and I did suggest considering shorter talk times. The presentations were 60 minutes this year, but I've seen successful conferences with shorter talks (45 min, even down to 30 min).

To help thank the speakers, and to help celebrate the conference, the JavaBin folks arrange an excursion called JourneyZone. About 16 total travelers left the conference after it was done and headed up north.

For many of us, it was our first time in Norway, and we were no disappointed. We traveled up to the MIT Fablab in Lyngen, Norway.


View Larger Map

We saw the northern lights, hiked a glacier, zoomed down a zip line, rappelled down a cliff, relaxed in a sauna, hung out with super cool people, and more. Here's the glacier we hiked across:




In summary, Dart was seemingly well received by the JavaZone audience, the JavaZone conference is first rate, and JourneyZone was spectacular. Thanks to all the organizers, and thanks to all those who attended my talk. Hope to see you again next year!

Thursday, September 5, 2013

You complete me, unless you already have a Dart future

Dart Protip: if you already have an instance of Future, you probably don't need a Completer. Simply return the last Future.



If you find yourself using Completers inside of Futures, like this:


  // NOT recommended.

  Future doStuff() {
    Future future = someAsyncProcess();
    Completer completer = new Completer();

    future.then((msg) {
      bool result = msg.result as bool;

      completer.complete(result);
    });

    return completer.future;
  }


then I'm happy to report there's a better way. Dart's Futures chain, so you can do this instead:


  // Recommend.
  Future doStuff() {
    return someAsyncProcess().then((msg) => msg.result);
  }


The last Future in a chain can return another Future, or simply a value. It's always a good idea to return a Future from a function that uses a Future. This way, the caller knows when the method finished its async work, and can properly handle potential errors.

Completers are great for bridging a callback-based API with a Future-based API. For example, suppose your database driver doesn't use Futures, but you need to return a Future. Try this code:

  // A good use of a Completer.

  Future doStuff() {
    Completer completer = new Completer();
    runDatabaseQuery(sql, (results) {
      completer.complete(results);
    });
    return completer.future;
  }

To learn more about Futures, read Using Future Based APIs. To step up your game, read Futures and Error Handling.

Enjoy!

Tuesday, July 23, 2013

Polymer and Dart: A First Look

In which I try the new polymer.dart, a build.dart file is deleted, and a template springs to life.

(This post heavily inspired by Nik Graf's great "Getting started with Polymer.dart" post.)

The web is evolving


Developers take notice! Coming soon: actual encapsulation, real live data binding, even re-usable components! The Web Components family of specifications is on their way to a browser near you, but the standardizations process is lengthy. Fear not, web engineers are working hard to bring these new capabilities to you today.

The Polymer project, a new type of library for the web, is built on top of Web Components, and designed to leverage the evolving web platform on modern browsers. Most importantly, the Polymer project has code that you can use today (to be fair, it's pre-alpha, but much of it works if you are brave).

The Polymer project is more than just polyfills.

Web UI is evolving


The Dart project, to more closely align with emerging web specifications, is building polymer.dart, the next evolution of Web UI. In the announcement, it was mentioned that Web UI will be sunset in favor of polymer.dart. While Web UI will be around for some time, new work focuses on polymer.dart.

From the Polymer project FAQ:

How is polymer.dart related to Polymer?
polymer.dart is a Dart port of Polymer created and maintained by the Dart team. The Dart team is collaborating with the Polymer team to ensure that polymer.dart elements and polyfills are fully compatible with Polymer.

Luckily for developers, polymer.dart and Web UI are fairly similar. It shouldn't be too much work to port a Web UI app to polymer.dart. We anticipate docs and examples to help developers with the transition.

There are many good reasons for this evolution:

  • closer alignment to another team building out support for Web Components
  • eventual elimination of the build/compile step required by Web UI
  • better interop between JavaScript components and Dart components
  • Dart developers get faster access to critical polyfills like Web Animations and Pointer Events

There's no rush for you to upgrade. Polymer.dart is very new, and it will be some time before it is feature compatible with Web UI. So, stick with Web UI if you're building production apps and wait for the transition guides to appear.

Aside: What about Angular?


Many developers want to know what's up with Angular, and how it all relates. I can't speak for the Angular project, but this post to the Polymer mailing list from Mi┼íko Hevery should shed some light:

We're in early stages of designing Angular 2.0, but some of our goals are: 
- Angular will use the underlying web platform features available to it (e.g. Node.bind, template integration, Custom Elements, etc...) 
- Web Components (Polymer, Ember, or any other framework/library) will work seamlessly within Angular apps and directives. 
- Components written in Angular will export to Web Components (to be used by Polymer, Ember, or any other framework/library) . 
We're working actively with the MDV, Web Components, and Polymer teams to make sure that our approaches remain compatible as all these projects evolve (and they will still evolve). 
-- Misko & the Angular team
I look forward to frameworks like Angular using the foundation of Web Components.

Hello, Polymer.dart


Let's give polymer.dart a try! For this first look, we'll build a very simple "Hello, World" example that shows off how to install polymer.dart, use the <template> tag, and bind a model.

Installing the polymer package


Add the polymer pub package to your pubspec.yaml. For this simple example, this is the only package that you'll need to explicitly declare:

name: polymer_first_look
dependencies:
  polymer: any


Creating the template


Add a <template> element to the web/index.html file. A <template> is an inert DOM fragment. It's valid HTML, but not activated by the page. You can manually clone the element and insert the clone into the page. Or, even better, you can use Model-Driven Views (MDV) to bind data to the template. The binding of data to the template activates the template, thus inserting it into the page.

The bind attribute on the <template> element states that the template activates (is cloned and inserted into the DOM) when data is bound to the template. The bind attribute comes from MDV.

The {{ and }} mark data binding between a field and some text. The field comes from the model data objects bound to the template. The {{ and }} are removed and replaced with the value of the field.

<!DOCTYPE html>

<html>
  <head>
    <title>index</title>
  </head>
 
  <body>   
    <template id="tmpl" bind>
      <p>Hello {{msg}}</p>
    </template>
    
    <script type="application/dart" src="index.dart"></script>
    <script src="packages/browser/dart.js"></script>
  </body>
</html>


Binding the data


The application, in web/index.dart, creates a single model object (a plain Dart object) and then attaches it to the <template> element from the page.

We must import the MDV package and initialize it before binding can have any effect.


import 'dart:html';
import 'package:mdv/mdv.dart' as mdv;

class Message {
  String msg;
}

main() {
  mdv.initialize();
  
  var message = new Message()..msg = 'world';
  query('#tmpl').model = message;
}


One big difference between Web UI and Polymer.dart: you must explicitly bind your model to the template. Web UI worked with code generation and lexical scope. Polymer.dart, because it comes from a JavaScript-influenced Polymer project, does not have code generation or lexical scope.

And that's it! There's no need for a build.dart with this simple test app. This works in Dartium and via dart2js. (Note: dart2js + polymer.dart output size is quite large, but follow dartbug.com/11524 for a fix.)

Next steps


This is just the beginning, there's a lot to explore with polymer.dart. I'm looking forward to more experiments as I learn about this brave new world.

Disclaimer

I'm probably required to say that the views expressed in this blog are my own, and do not necessarily reflect those of my employer. Also, except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 3.0 License, and code samples are licensed under the BSD License.