Tuesday, October 7, 2014

Generating Super Simple Polymer (JS)


I really want a minimal Polymer code generator. I thought Yeoman might have something to fit my needs, but upon further review, I am not so sure.

I missed some of what Yeoman's Polymer was doing yesterday. Most the sub-generators wind up installing a lot. I had thought yo polymer:seed might be the answer to my needs. Even though it installed testing stuff too soon (and the wrong testing library) for my tastes, the rest was close. But it turns out that it just installs crazy amounts of dependencies in the parent directory thanks to the hidden .bowerrc file that it includes:
{
  "directory": "../"
}
The end result is 9 directories containing 52 files:
$ tree       
.
├── core-component-page
│   └── ...
├── elements
│   ├── bower.json
│   ├── demo.html
│   ├── index.html
│   ├── README.md
│   ├── test
│   │   ├── index.html
│   │   ├── tests.html
│   │   └── x-pizza-basic.html
│   ├── x-pizza.css
│   └── x-pizza.html
├── platform
│   ├── platform.js
│   └── ...
├── polymer
│   ├── polymer.html
│   ├── polymer.js
│   └── ...
└── polymer-test-tools
    ├── chai
    │   └── ...
    ├── karma-common.conf.js
    ├── mocha
    │   ├── mocha.js
    │   └── ...
    └── ...

9 directories, 52 files
My goal here is to generate the minimal setup required to start coding a Polymer element. I need to keep it small so that my screencasts are short—both on time and concepts.

The end result of my first screencast should be something like:
$ tree
.
├── index.html
├── elements
│   ├── x-pizza.html
│   └── x_pizza.js
├── bower.json
└── bower_components
    └── ...
I should be able to run a python simple HTTP server in the root and see the <x-pizza> element working in the index.html page.

So, I am going to let Emacs generate the index.html page. I will likely stick with a very simple HTML skeleton and add the platform.js <script> tag and the element link by hand (or keyboard shortcut). I tend to think of these as important enough to type out by hand.

For the Bower stuff, I think I will approach it by creating a minimal bower.json on the command-line:
$ echo '{"name": "x_pizza"}' > bower.json
Then install and save my Polymer dependencies:
$ bower install -S Polymer/polymer
bower polymer#*                 cached git://github.com/Polymer/polymer.git#0.4.2
...
polymer#0.4.2 bower_components/polymer
So that takes care of the index.html page and Bower, what about the element? The polymer:el generator is close to what I need, but... there are two things that give me pause.

First, the element is generated in the wrong directory (I think). Where I would prefer it go directly in elements, polymer:el puts it in app/elements/<name-of-element>/:
$ yo polymer:el x-pizza
? Would you like an external CSS file for this element? No
? Would you like to include an import in your elements.html file? No
   create app/elements/x-pizza/x-pizza.html
I could move it, but then I would have to change the polymer.html import in the generated output since bower_components will wind up in the parent directory instead of the grandparent directory:
<link rel="import" href="../../bower_components/polymer/polymer.html">
<polymer-element name="<%= elementName %>" attributes="">
  <template><% if (!externalStyle) { %>
    <-- ... -->
  </template>
  <script>
    (function () {
      'use strict';

      Polymer({
        // define element prototype here
      });

    })();
  </script>
</polymer-element>
Even that I could live with, but I think including the Polymer prototype definition in the HTML, coupled with my other pet peeves, is what will keep me from Yeoman. Emacs has a nifty JavaScript mode that I would prefer to use in a separate .js file. There are multi-minor mode solutions for working with both HTML and JavaScript in the same file, but something about it just does not feel as nice.

So I think I will wind up relying on Emacs for this as well. Speaking of Emacs, I need to dust off some of my extremely rusty elisp to modify the built-in HTML mode to accommodate <polymer-element> definition elements. Something like this in my .emacs ought to work:
(setq html-tag-alist (cons '("polymer-element" (\n
        "<template>\n    " _
        "\n  </template>\n"
        "  <script src=\"x_element.js\"></script>"
  ))
 html-tag-alist
))
When I used the HTML mode insert-element command for polymer-element, the above template will be inserted with the cursor being placed after the initial <template> tag (by virtue of the underscore). I may fiddle with yasnippet as an alternative, but I have never been able to get on board with snippets. I can remember the most obscure Emacs keyboard combinations, but I have never been able to use snippets often enough to remember them.

Regardless, I think that I will not be using Yeoman in the screencasts. It sets up beautiful Polymer elements and development layouts, but feels too noisy for my needs. Hopefully this low-key approach will work better.

Day #206

Monday, October 6, 2014

Simple Polymer Generators with Yeoman


I shaved 3 minutes off the Patterns in Polymer screencast that deals with the initial Polymer element generation in Dart. I still need to find another 3 minutes, but that is a good start. Good enough that I would to explore doing the same with JavaScript Polymer elements. Since I would like to stick with standard, maintained generators, this means Yeoman.

In all honesty, I have never used Yeoman. I tend to prefer writing initial applications / widgets by hand. I regard code that I maintain directly as too important to trust without hand-writing. If code is a starship, then I side with Sarris that a captain “know every bolt, every weld in his ship.” I might treat the inner workings of bolt creation and welding tools as abstractions that I can delve into later, but I will understand them well enough and know where they reside in my code.

That said, I don't hate code generation. Especially if they save me 3+ minutes in screencasts.

So I start by installing Yeoman:
$ npm install -g yo
/home/chris/local/node-v0.10.20/bin/yo -> /home/chris/local/node-v0.10.20/lib/node_modules/yo/cli.js

> yo@1.3.0 postinstall /home/chris/local/node-v0.10.20/lib/node_modules/yo
> node ./scripts/doctor

[Yeoman Doctor] Everything looks alright!

yo@1.3.0 /home/chris/local/node-v0.10.20/lib/node_modules/yo
├── ...
└── yeoman-generator@0.17.7 (...)
Next, I install the Polymer generator for Yeoman:
$ npm install -g generator-polymer
...
generator-polymer@0.5.1 /home/chris/local/node-v0.10.20/lib/node_modules/generator-polymer
...
With that, I am ready to run the generator in a new (temporary) project:
$ cd ~/tmp
$ mkdir x_pizza
$ cd !$
$ yo polymer
? Would you like to include core-elements? No
? Would you like to include paper-elements? No
? Would you like to use SASS/SCSS for element styles? No
   create .gitignore
   create .gitattributes
   create .bowerrc
   create bower.json
   create .jshintrc
   create .editorconfig
   create Gruntfile.js
   create package.json
   create app/404.html
   create app/favicon.ico
   create app/robots.txt
   create app/styles/main.css
   create app/scripts/app.js
   create app/.htaccess
   create app/elements/elements.html
   create app/elements/yo-list/yo-list.html
   create app/elements/yo-list/yo-list.css
   create app/elements/yo-greeting/yo-greeting.html
   create app/elements/yo-greeting/yo-greeting.css
   create app/index.html
   create app/test/index.html
   create app/test/tests.html
   create app/test/yo-greeting-basic.html
   create app/test/yo-list-basic.html
...
Yikes! That is a lot of infrastructure. I would rather focus on teaching Polymer than Polymer-on-Yeoman in these screencasts. The default page in app/index.html file and the sample Polymer element that it includes are nice demos, but I have the feeling that I may end up deleting a bunch of this for use in the screencast. On the other hand, it is nice that it creates a useful bower.json and runs bower.json. I will have to consider this.

The element generator is pretty clean however:
$ yo polymer:el x-pizza
? Would you like an external CSS file for this element? No
? Would you like to include an import in your elements.html file? No
   create app/elements/x-pizza/x-pizza.html
$ cat app/elements/x-pizza/x-pizza.html
<link rel="import" href="../../bower_components/polymer/polymer.html">
<polymer-element name="x-pizza" attributes="">
  <template>
    <style>
      :host {
        display: block;
      }
    </style>
  </template>
  <script>
    (function () {
      'use strict';
      Polymer({
        // define element prototype here
      });
    })();
  </script>
</polymer-element>
I might quibble with the Polymer class being inline instead of in a separate script file, but that is close to what I want.

But in the end, I think the seed-element generator is closer to what I need for the screencasts. There is less infrastructure, but it still generates a smoke test page and a skeleton for the template:
$ cd ..    
$ rm -rf x_pizza 
$ mkdir x_pizza
$ cd !$
$ yo polymer:seed

     _-----_
    |       |    .--------------------------.
    |--(o)--|    | Out of the box I include |
   `---------´   |        the Polymer       |
    ( _´U`_ )    |       seed-element.      |
    /___A___\    '--------------------------'
     |  ~  |     
   __'.___.'__   
 ´   `  |° ´ Y ` 

? What is your GitHub username? eee-c
? What is your element's name: x-pizza
   create .gitignore
   create .gitattributes
   create .bowerrc
   create bower.json
   create .jshintrc
   create .editorconfig
   create x-pizza.css
   create x-pizza.html
   create index.html
   create demo.html
   create README.md
   create test/index.html
   create test/x-pizza-basic.html
   create test/tests.html
...
This also runs bower install, so I will be ready to start coding right away. The demo.html page that uses the generated <x-pizza> element is welcomingly brief. The generated element, however, is crazy long—it even includes a fireLasers() method.

So it does not seem that generating scaffold code in JavaScript is going to be as easy in JavaScript as in Dart. Hopefully I can work with it to keep the running time under 10 minutes. Time will tell.


Day #205

Sunday, October 5, 2014

Command-Line Polymer.dart Generators


I am working on the second in the series of screencasts for readers of the “Extras” edition of Patterns in Polymer. Try as I might, I cannot get it down to under 12 minutes. I would much prefer that they run ~5 minutes, so tonight I explore alternate approaches to code what I need.

The goal of this screencast is to build a working Polymer element. I am not trying to get all functionality in place in this screencast. Rather, I am trying to get everything in the right place to build the real thing. For the Dart version of the screencast, I can code up the pubspec.yaml, web/index.html, x-pizza.html, and x_pizza.dart files in 15 minutes. I could probably get that down to 12 minutes, but I need help if I am going to get to 5-8 minutes.

I could speed up certain sections of the video or use some pre-written snippets, but I have a slight preference for keeping it real-time. So that means code generation. I would further prefer to use standard generators rather than hand-writing my own. Fortunately, Polymer.dart has a few built-in.

I start by globally registering Polymer for running scripts:
$ pub global activate polymer
Resolving dependencies... (30.5s)
...
Activated polymer 0.15.0+1.
Not sure what's up with the 30.5 seconds. Hopefully that's just an internet glitch.

Before using any of the generators, I need a project to play about in. I create a temporary directory with a pubspec.yaml:
$ mkdir tmp
$ cd !$
$ echo "name: x_pizza" > pubspec.yaml
There are two bin scripts in Polymer 0.15.0+1:
$ ls ~/.pub-cache/hosted/pub.dartlang.org/polymer-0.15.0+1/bin
new_element.dart  
new_entry.dart
I will start by creating a new element, which supports the following command-line options:
$ pub global run polymer:new_element -h
pub run polymer:new_element [-o output_dir] [-e super-element] element-name
-o, --output-dir    Output directory
-e, --extends       Extends polymer-element or DOM element (e.g., div, span)
-h, --[no-]help     
So let's create an <x-pizza> element:
$ pub global run polymer:new_element -o lib/elements x-pizza
Successfully created:
  /home/chris/tmp/lib/elements/x_pizza.dart
  /home/chris/tmp/lib/elements/x_pizza.html
And that does exactly what I would expect (and need). The Dart class definition for the element has the boilerplate stuff:
$ cat lib/elements/x_pizza.dart
import 'package:polymer/polymer.dart';
/**
 * A Polymer x-pizza element.
 */
@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  /// Constructor used to create instance of XPizza.
  XPizza.created() : super.created() {
  }
  // Commented out lifecycle methods here...
}
That alone will save me 30 seconds of typing. Compound it with the HTML template definition and I am getting closer to what I need:
$ cat lib/elements/x_pizza.html 
<!-- import polymer-element's definition -->
<link rel="import" href="../../../packages/polymer/polymer.html">
<polymer-element name="x-pizza">
  <template>
    <style>
      :host {
        display: block;
      }
    </style>
    <!-- Template content here -->
  </template>
  <script type="application/dart" src="x_pizza.dart"></script>
</polymer-element>
It sure would be nice if I could generate some of the web/index.html smoke test page. And add the Polymer dependency and transformer to my pubspec.yaml, which is still just:
$ cat pubspec.yaml 
name: x_pizza
Say, maybe that's what the other generator does:
$ pub global run polymer:new_entry -h            
pub run polymer:new_entry entry_point_file.html
Well, that help output leaves a lot to the imagination. Fortunately, it too does exactly what I would expect (and need). Well almost:
$ pub global run polymer:new_entry web/index.html
Added web/index.html to /home/chris/tmp/pubspec.yaml
Successfully created:
  /home/chris/tmp/web/index.html
As the output indicates, I now have a Polymer transformer pointing to the smoke test page:
$ cat pubspec.yaml                               
name: x_pizza
transformers:
- polymer:
    entry_points:
    - web/index.html
A nice-to-have would be adding the Polymer dependency to pubspec.yaml, but this will already help some (especially since I keep omitting the “s” at the end of “transformers” for some reason).

What will help even more is the boilerplate smoke test page at web/index.html:
$ cat web/index.html 
<!doctype html>
<html>
  <head>
    <script src="packages/web_components/dart_support.js"></script>
    <!-- link rel="import" href="path_to_html_import.html" -->
  </head>
  <body>
    <!-- HTML for body here -->
    <script type="application/dart">export 'package:polymer/init.dart';</script>
  </body>
</html>
I still have to link to the lib/x_pizza.html import in there, but that will certainly save some time and typing.

All-in-all, I am excited about these Polymer Dart generators. I expect they will go a long way to solving my screencast time-crunch. Best of all, they are built right into the Polymer.dart library. Now I just need to find a way to do this with the JavaScript version of the screencast. It may be time to play with the new Yeoman generators. Tomorrow.


Day #204

Saturday, October 4, 2014

Testing Computed Style in Dart


I was able to test CSS styling last night, making getComputedStyle() my new best friend. Seriously, I have wanted to do just this kind of testing before, but never knew it was possible. Which begs the question, is it possible in Dart?

Presumably, it is. Especially since getComputedStyle() is part of core dart:html. Even better, getComputedStyle() in Dart is available on any Element, so no need to run it through the top-level window version.

As yesterday, I am testing some Bootstrap styles applied to a custom Polymer element for a chapter in Patterns in Polymer. More precisely, I am using Polymer.dart for the Dart version of this chapter.

The setup for testing this creates the <pricing-plan> elements that should use Bootstrap styles:
  var _el;
  group("styles", (){
    setUp((){
      _el = createElement('''
        <pricing-plans>
          <pricing-plan type="primary">plan content</pricing-plan>
        </pricing-plans>
      ''');
      document.body.append(_el);
    });
    // Test goes here...
  });
The type="primary" attribute will generate a Bootstrap panel-primary class that results in a panel with a blue heading like the one on the right:



To test this, I need the same computed style as from last night's JavaScript test. I need grab the <pricing-plan> element, then query for the panel heading, and finally ask for the computed style:
    test("come from external sources", (){
      var primary_el = _el.query('pricing-plan'),
          heading_el = primary_el.shadowRoot.query('.panel-heading'),
          style = heading_el.getComputedStyle();

      expect(style.backgroundColor, 'rgb(66, 139, 202)');
    });
The expectation is then that the panel's background color will be default Bootstrap primary blue. But this turns out not to work:
CONSOLE MESSAGE: FAIL: styles come from external sources
  Expected: 'rgb(66, 139, 202)'
    Actual: 'rgba(0, 0, 0, 0)'
     Which: is different.
Interesting, I note that the smoke test web page is making XHR requests for Bootstrap:
XHR finished loading: GET "http://localhost:8080/packages/bootstrap_example/assets/bootstrap.min.css".
This occurs even though Bootstrap is being imported with @import in the Polymer definition:
<link rel="import" href="../../../packages/polymer/polymer.html">
<polymer-element name="pricing-plan">
  <template>
    <div class="col-md-{{size}}"><!-- ... --></div>
    <style>
      @import '/packages/bootstrap_example/assets/bootstrap.min.css';
    </style>
  </template>
  <script type="application/dart" src="pricing_plan.dart"></script>
</polymer-element>
I presume this is the Polymer transformer doing this. Since I cannot change it, I need a workaround. Like running pub serve:
$ pub serve
Loading source assets... 
Loading polymer transformers... 
Serving bootstrap_example web  on http://localhost:8080
Serving bootstrap_example test on http://localhost:8081
The tests are kindly served up from 8081, so I point content_shell there:
$ content_shell --dump-render-tree http://localhost:8081                         
CONSOLE MESSAGE: unittest-suite-wait-for-done
CONSOLE MESSAGE: PASS: styles come from external sources
CONSOLE MESSAGE: PASS: [defaults] name is "Plan"
CONSOLE MESSAGE: PASS: [defaults] can embed code
CONSOLE MESSAGE: 
CONSOLE MESSAGE: All 3 tests passed.
CONSOLE MESSAGE: unittest-suite-success
CONSOLE WARNING: line 213: PASS
So yay! Aside from the tiny XHR curveball with the @import CSS, it is just as easy to test styles in Polymer.dart as in the JavaScript version.


Day #203

Friday, October 3, 2014

Testing CSS Colors in Polymer


I have 5 whole tests covering the functionality of the Bootstrap styled Polymer element. And it is occurs to me that not a single one of those tests has anything to do with the intention of this chapter in Patterns in Polymer, which is styling Polymer elements.

The Polymer element in this case is a simple pricing plans element:



So it seems to me that I ought to be able to test that one of the individual pricing plans in the test is blue.

I have to fiddle with the setup, but I eventually get the following Jasmine test to work:
  describe('defaults', function(){
    var el;
    beforeEach(function(done){
      el = document.querySelector('pricing-plans');
      setTimeout(done, 10);
    });

    it('gets styles from external sources', function(){
      var primary_el = el.querySelectorAll('pricing-plan')[2],
          heading_el = primary_el.shadowRoot.querySelector('.panel-heading'),
          style = window.getComputedStyle(heading_el);

      expect(style.backgroundColor).toEqual('rgb(66, 139, 202)');
    });
  });
This finds the last <pricing-plan> element in my fixture file:
<pricing-plans>
<pricing-plan name="pricing plan 01">desc 01</pricing-plan>
<pricing-plan name="pricing plan 02">desc 02</pricing-plan>
<pricing-plan type="primary" name="pricing plan 03">desc 03</pricing-plan>
</pricing-plans>
The last element has the primary type set which Bootstrap will color blue by default. I had originally tried to grab the <pricing-plan> element directly to test, but found that I had to dive into that shadow DOM to grab the panel header to find the element that actually is blue.

The test then uses the getComputedStyle() method on window to find the background color of this element, which should be the specified shade of blue.

Among the tricks that I need to get this working is the setTimeout() dance in this test's setup—this gives the page time to load Bootstrap CSS and apply it (I am running this with Karma, so loading from a server does work). If I opt to use the apply-author-styles Polymer tag, I will also have to apply Bootstrap CSS to the main testing page. But, since this is just the @import solution for external CSS inside a Polymer element, this is all I need for tonight.

The end result is that the test passes:
...
SUCCESS  gets styles from external sources
Skipped 0 tests
And, more importantly, the test fails if I change the fixture from a panel-primary to a panel-success:
FAILED  gets styles from external sources
Expected 'rgb(223, 240, 216)' to equal 'rgb(66, 139, 202)'.
I was unsure how easy it would be to accomplish this, but testing computed CSS in Polymer elements turns out to be fairly clean and easy.


Day #202

Thursday, October 2, 2014

Custom_element_apigen and Using JavaScript Polymer Elements in Dart


I had little luck using my apply-author-styles JavaScript Polymer element in Polymer.dart last night. Well, I had great luck if you count hand-editing Core Elements in order to get it to work. Personally, I do not count that as good luck.

What I do count as good luck is readers like James Hurford writing to tell me about potential solutions that I may have overlooked. In this case, I have not tried the custom_element_apigen Dart Pub package, which “lets you wrap custom elements written with polymer.js and provide a Dart API for them.” I do not necessarily need a Dart API, but only the ability to use <apply-author-styles>. Still, it seems worth a try.

To install the JavaScript <apply-author-styles> last night, I had introduced a project-level .bowerrc file to install it in lib/bower_components:
{
  "directory": "lib/bower_components"
}
The custom_element_apigen package would rather this go into lib/src:
{
  "directory": "lib/src"
}
I already have a bower.json that includes apply-author-styles as a dependency:
{
  "name": "bootstrap_example",
  // ...
  "dependencies": {
    "bootstrap": "~3.1.0",
    "apply-author-styles": "eee-c/apply-author-styles"
  }
}
So I remove last night's install (hand edits and all):
$ rm -rf lib/bower_components
And reinstall into custom_element_apigen's preferred location:
$ bower install
...
bootstrap#3.1.1 lib/src/bootstrap
└── jquery#2.1.1

apply-author-styles#434679b8da lib/src/apply-author-styles
├── core-ajax#0.3.6
└── polymer#0.3.6
It was ultimately the core-ajax and polymer dependencies that did me in last night. Core-ajax depends on core-xhr, which requires the JavaScript version of Polymer instead of the Dart version. Hopefully custom_element_apigen can work around this.

I add custom_element_apigen to the list of Dart dependencies in my pubspec.yaml:
name: bootstrap_example
dependencies:
  polymer: any
  custom_element_apigen: any
dev_dependencies:
  unittest: any
transformers:
- polymer:
    entry_points:
      - web/index.html
      - test/index.html
Then I get the new dependencies:
$ pub get
Resolving dependencies...
+ custom_element_apigen 0.0.3
Downloading custom_element_apigen 0.0.3...
Changed 1 dependency!
The documentation is upfront about not including much information. It does not even say if the configuration file is generated or created by hand. I try the former:
$ pub run custom_element_apigen:update configfile.yaml
Parsing configuration ... error: file configfile.yaml doesn't exist
OK. I need to build this by hand. Per what documentation there is, I copy the configuration file from core-elements. I start with this:
files_to_generate:
  - apply-author-styles/apply-author-styles.html
Which seems to be what custom_element_apigen wants:
$ pub run custom_element_apigen:update apply_author_styles_gen.yaml
<link rel="import"
^^^^^^^^^^^^^^^^^^
1 of 1: lib/src/apply-author-styles/apply-author-styles.html[Warning from polymer (Linter) on bootstrap_example|lib/src/core-ajax/core-xhr.html]:
line 25, column 1 of lib/src/core-ajax/core-xhr.html: Missing definition for <polymer-element>, please add the following HTML import at the top of this file: <link rel="import" href="../../../../packages/polymer/polymer.html">. See http://goo.gl/5HPeuP#polymer_3 for details.
<polymer-element name="core-xhr" hidden>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Done
I am unsure of those warnings, but I give it a try anyway. In my Polymer.dart element, I replace last night's bower_components attempt:
<link rel="import" href="../../../packages/polymer/polymer.html">
<link rel="import"
      href="../bower_components/apply-author-styles/apply-author-styles.html">
<polymer-element name="pricing-plan">
  <template><-- ... --></template>
  <script type="application/dart" src="pricing_plan.dart"></script>
</polymer-element>
Using instead the custom_element_apigen element:
<link rel="import" href="../../../packages/polymer/polymer.html">
<link rel="import"
      href="../apply_author_styles.html">
<polymer-element name="pricing-plan">
  <template><!-- ... --></template>
  <script type="application/dart" src="pricing_plan.dart"></script>
</polymer-element>
But, when I fire this up in Dartium, I still get:
GET http://localhost:8080/packages/bootstrap_example/core_ajax.html 404 (Not Found) 
It seems that I need to include core-ajax (and core-xhr) in the list of generated files:
files_to_generate:
  - apply-author-styles/apply-author-styles.html
  - core-ajax/core-ajax.html
  - core-ajax/core-xhr.html
After re-running pub run custom_element_apigen:update apply_author_styles_gen.yaml, I again try this in Dartium. Unfortunately, I have no more success this time:
Internal error: 'package:bootstrap_example/core_xhr.dart': error: line 33 pos 29: duplicate formal parameter 'inOptions'
  request(inOptions, String inOptions.url, String inOptions.method, bool inOptions.sync, inOptions.params, inOptions.body, inOptions.headers, String inOptions.responseType, bool inOptions.withCredentials, inOptions.callback) =>
                            ^ 
Bleh. I think it safe to say that I understand why core-xhr in Dart is still hand-written in Dart instead of wrapped by custom_element_apigen. I will call it a night here.

Although unsuccessful in getting apply-author-styles to work, it was nice to get a feel for what custom_element_apigen does. Hopefully in the near future it can also generate a Dart API for core-xhr.


Day #201

Wednesday, October 1, 2014

Bootstrap CSS with Polymer (Revisited)


I maintain that there are times that developers will want web components to look like the rest of their site, not like Material Design. Toward that end, I include a chapter on Bootstrap CSS in Patterns in Polymer. Today, I double check that my solution stills works after upgrading all code the latest versions of Polymer.dart (for the Dart version of the book) and Polymer (for the JavaScript version of the book).

And, thankfully, my solution does continue to work:



One could certainly argue that this solution trades one popular-on-the-verge-of-overused styling approach for another. I would probably agree. The point is not to use a specific style, just to use a common style to validate an approach.

At the risk of spoiling the joy of reading the chapter, the solution boils down to an @import of the common CSS:
<link rel="import" href="../bower_components/polymer/polymer.html">
<polymer-element name="pricing-plan" attributes="name type size">
  <template>
    <div class="col-md-{{size}}"><!- ... --></div>
    <style>
      @import '../bower_components/bootstrap/dist/css/bootstrap.min.css';
    </style>
  </template>
  <script src="pricing_plan.js"></script>
</polymer-element>
There is a little more to the solution (so really, you should read the book), but that is the gist.

But this got me thinking, perhaps the <apply-author-styles> custom Polymer element would work as well. So, in the JavaScript version of the code, I install it with Bower:
$ bower install --save eee-c/apply-author-styles
...
bower apply-author-styles#*               install apply-author-styles#434679b8da
bower core-ajax#~0.3.2                    install core-ajax#0.3.6

apply-author-styles#434679b8da bower_components/apply-author-styles
├── core-ajax#0.3.6
└── polymer#0.4.0

core-ajax#0.3.6 bower_components/core-ajax
└── polymer#0.4.0
Then, in my Polymer element HTML definitions, I remove the @import style and instead add the <apply-author-styles> tag:
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import"
      href="../bower_components/apply-author-styles/apply-author-styles.html">
<polymer-element name="pricing-plan" attributes="name type size">
  <template>
    <apply-author-styles></apply-author-styles>
    <div class="col-md-{{size}}"><!-- ... -></div>
  </template>
  <script src="pricing_plan.js"></script>
</polymer-element>
And that works perfectly:



That is nice. I do not know if I will offer that as the solution to applying external styles in Patterns in Polymer, but it seems worth a mention. Worth it if it works in Dart as well.

So I switch over to the Dart version of the chapter code, which is configured to install Bower components in lib/elements by way of a .bowerrc file:
$ cat .bowerrc
{
  "directory": "lib/bower_components"
} 
I again execute bower install --save eee-c/apply-author-styles, this time in my Dart project.

I update my element to use the Bower installed <apply-author-styles>:
<link rel="import" href="../../../packages/polymer/polymer.html">
<link rel="import"
      href="../bower_components/apply-author-styles/apply-author-styles.html">
<polymer-element name="pricing-plan">
  <template>
    <apply-author-styles></apply-author-styles>
    <div class="col-md-{{size}}"><!-- ... --></div>
    <!-- <style> -->
    <!--   @import '/packages/bootstrap_example/assets/bootstrap.min.css'; -->
    <!-- </style> -->
  </template>
  <script type="application/dart" src="pricing_plan.dart"></script>
</polymer-element>
But run into problems that I have seen before. Actually, when I saw this previously, the error was much more helpful. This time, all I get is:
Uncaught TypeError: undefined is not a function    polymer.js:96
  (anonymous function)                             polymer.js:96
  (anonymous function)                             polymer.js:99
And:
Exception: NoSuchMethodError: method not found: 'whenPolymerReady'
Receiver: Instance of 'JsFunction'
Arguments: [Closure: () => dynamic]               package:polymer/src/loader.dart:103
Eventually, I realize that the polymer.js generating the error is the one installed via Bower, not the one bundled with Polymer.dart. So I have to do what I did before: hand-edit core-xhr.html in lib/bower_components to point to the Polymer.dart version. Horrible.

I do not think this is easily solved. It would seem that any JavaScript Polymer element that depends on core-xhr and, by extension, core-ajax, will not be usable in Polymer.dart. Since <apply-author-styles> is such a beast, it would seem that I am out of luck, leaving the @import solution as the best and only available. Unless I am willing to rewrite and maintain apply-author-styles in Dart as well...



Day #200