Skip to content
This repository has been archived by the owner on Dec 19, 2017. It is now read-only.

Transparently Consume Material Elements in Dart

Kathy Walrath edited this page Sep 15, 2015 · 3 revisions

This document organizes a collection of design documents with the purpose of making it as easy as possible to consume custom elements, especially the material custom elements, in Dart programs. Please note that the examples in these documents are for illustration purposes, refer to the actual implementation of each package to learn the most recent API.

Motivation

The polymer.dart project has historically taken many roles:

  • bring to Dart the power of the next web generation standards, including features such as shadow-dom, custom-elements, and HTML imports
  • make it easy to consume elements that use these features from Dart
  • bring a set of custom elements that follow the material design to Dart
  • make it easy to write new custom elements using the Polymer paradigm in Dart

Except for the last goal, all other goals are orthogonal to polymer itself. However, our implementation currently mixes all these pieces together and doesn't provide good encapsulation for Dart developers. For example, if a developer wants to consume material design elements today, he needs to first learn how to use polymer.dart and be aware of several details of HTML imports and code transformers before he can use them.

Our goal is to make the process of consuming material elements simpler and more transparent.

Example use case

Here is an example of how we envision users will be able to write code to use a paper element in their app. Assuming they are not using polymer themselves, their HTML page is as simple as any HTML page that loads some Dart code:

index.html:

<script type="application/dart" src="index.dart"></script>

Their Dart code imports a paper element and instantiates it in code. Unfortunately, there is no static initialization in the Dart language, so we ask them to include a couple lines of code to ensure the initialization of all web components:

index.dart:

import 'dart:html';
import 'packages:paper_elements/paper_dialog.dart';
import 'packages:initialize/initialize.dart' as init;

main() => init.run().then((_) {
  document.body.append(new PaperDialog()..heading = "Hi there");
});

At this point, their application can run in development mode in Dartium. To deploy their application, they include a transformer section in their pubspec to indicate that web components are being used:

pubspec.yaml:

name: my_app
dependencies:
  paper_elements: ">0.5.0 <0.6.0"
  web_components: ">0.11.0 <0.12.0"


transformers:
- web_components

And that's all there is to it. Under the hood, however, there is a lot of machinery to make this work:

  • we use a new Dart-to-HTML import feature to transparently load HTML definitions for paper elements.
  • we execute static initializers to load the paper-dialog implementation and to register the corresponding proxies.
  • we provide a mirror-based implementation that supports these features without using code transformations for development.
  • we transform the application for deployment in multiple phases, including phases to inline HTML imports, combine script tags, and replace reflective initialization with code generation.

Design Details

Our design is based on the following principles:

  • encapsulate as much as possible how material elements are exposed in Dart
  • expose features separately, so that users only need to learn the pieces they will be using
  • make features composable, so packages like web_components and polymer.dart can combine features that are commonly used together.

To this end, we propose a layered design with several new pieces and changes to existing pieces of our infrastructure. This includes:

  • adding generalized support for static initialization in Dart.
  • revisiting HTML imports: making them more independent of the rest of polymer, and adding support for Dart-to-HTML imports.
  • splitting the polymer codebase to make material elements independent of polymer.dart.

We'll discuss each in more detail below.

Static Initialization

Under our new design, we require static initializations for two purposes: to use a declarative pattern to register custom elements and custom element proxies, and to implement Dart-to-HTML imports. Note that the former is something we already do in polymer.dart, so this proposal generalizes some of the practices we already have.

We decided to divide support for static initialization in two layers:

  • Declarative initialization: Support for static initializers in purely Dart code. Without language support for static initialization, we will introduce this feature as a package.
  • Initialization from HTML: Apply static initialization to Dart in the context of HTML documents. This document specifies how to support multiple script tags, HTML imports, and Dart-to-HTML imports.

This separation keeps the design simpler and enables us to use static initialization to implement Dart-to-HTML imports internally. Also, if static initializers ever become a language feature, that would simply replace the first layer, but leave most of the second layer intact.

HTML imports

HTML imports are the main mechanism used to deliver and share paper elements. We present a design document on Dart-to-HTML import support, which details how we plan to support declaring HTML imports from Dart files. This feature will help reduce the redundancy between Dart and HTML imports in applications today.

Code organization

We want to minimize the dependencies needed to use paper elements. There are a few places where we can improve this:

  • Make transformers composable and independent of each other: our current architecture mixes several transformations under the polymer.dart transformer. It's not clear how to use portions of those transformers separately from the rest.
  • Today, users of paper elements depend on polymer.dart and all its transitive dependencies. For most elements only polymer.js is needed. We would like to explore splitting polymer.dart in two packages, so the polymer.js contents are distributed separately. One of the key challenge here is that some elements had to be ported (e.g core-list-dart, core-ajax-dart, core-localstorage-dart, core-xhr-dart).
  • Move some other pieces from polymer.dart out to the web_components package. For example, html inlining, and the polyfill injection transformer.