Saturday, November 30, 2013

I am really digging the possibilities opened up by UI-less Polymer elements. So far I found them super easy ways to establish event sinks or transforms on the documents in which they are inserted. They also make for a quick means of normalizing DOM events for tags that lack a nice interface. The possibilities are so wide open that it is hard to get a good sense of what makes for a good practice. But, since all of this is research material for a book named Patterns in Polymer, I need to develop that sense of best practice quickly.

Tonight, I am going to create an element that wraps last night's element. Whereas last night's <change-sink> wrapped contenteditable and <input> tags in a sane change event wrapper:

Tonight's should wrap the normalized change events from <change-sink> and save them for later use. The question that I would like to answer is, should I wrap elements like <change-sink> or is it better practice to combine change normalization and storage into a single element? I do not think that I will find a good answer tonight. Best practices are rarely found in a single day. Instead, I only hope to put in the necessary groundwork to answer that question down the road.

I start by importing the still-to-be-created store-changes.html into my web page and wrapping the <change-sink> element inside of the Polymer that will be defined:

Of note here is that Polymer's addEventListener() appears to bind this to the current instance of the Polymer. That is, if I was to reference the current Polymer object, I can use this without having to explicitly bind it inside the addEventListener().

That certainly looks like something that I could wrap in yet another Polymer to yield rudimentary undo/redo (maybe even with an actual UI!).

So far, I have to say that I like the separate elements like this. It feels very much like a convenient way of realizing separation of concerns in my code. That said, this could easily lead to deeply nested code and weird contextual behaviour depending on whether or not elements are parent, sibling, or child of other elements. And, since there are other ways to separate concerns, this seems a topic worth further exploration tomorrow.

Friday, November 29, 2013

Yesterday, I managed to scrape together a Polymer (JS) element that listened for events on the containing document. This was a non-UI element that was just in place to interact with other elements on the page. I think there is a lot of potential for this kind of thing, which is only one of the reasons that Polymer excites me enough to be writing a book on it.

Today, I hope to try the opposite. Instead of listening for events on the web page in which my custom Polymer element is contained, I would like to listen for events on an element inside my custom Polymer element. The idea in this case is to take something that is very hard to listen for events on, say a contenteditable <div>, and normalize them for easier consumption.

That does the trick as I now see the contenteditable contents change with each keyup:

<h1>Change e!</h1>
<h1>Change Me!</h1>

Instead of simply generating console messages, I want to support the API described earlier. When a change occurs, I should fire a "change" event whose details include the previous values for comparison. That is easy enough:

I support a was property on the Polymer element itself. The previous value is then assigned there whenever a change occurs. Just before that assignment, I fire the change event with the previous value of the contained element.

And that works just fine! When I make my first change, the was value is undefined and, after the second change, the was value is defined properly:

Was:
undefined
But now it's different!
Was:
<h1>Change Me!</h1>
But now it's different!

OK. That is pretty cool. Cool, but it is not actually firing on changes—just keyup. I'll get change events when I arrow key around my content editable <div>. I'll also get events with every character typed, not with every change. I am not going to see change events when content is pasted. Also, it would be cool if I could "change sink" <input> changes as well as content editable <div> tag changes.

All of that turns out to be pretty easy. I need to listen for a few more event types, use a debounce, and check for content in "value" if it is not in innerHTML:

That does the trick. It now works with <textarea> just as easily as it does with content editable <div> tags. I can paste and see a change event. I can type new content, but only see one event. It is all pretty slick.

The actual heavy lifting comes from Polymer establishing the custom element and its relationship with the child element. Once that is in place, normalizing an otherwise ugly event proves very straight forward. Best of all, this is built for re-use. Anyone with this <change-sink> code now has normalized change events. I am eager to see what else I can do with this stuff!

Thursday, November 28, 2013

When I think of web components, I normally think of creating new UI elements. But I think one of the more intriguing things that is possible with them is the ability to create UI-less features that simply serve as a convenient way to get access to streams of information. Instead of creating JavaScript objects that receive AJAX responses, create a Polymer element that does and then bubbles events in the document like an HTML element.

To explore the idea, I am going to try to create a JavaScript Polymer element that listens for click events on a web page. I start with the usual <script> setup and a custom <click-sink> element in the body:

And that works. When I click the page, I see "click" logged to the JavaScript console. It is of little use unless I transform that in some way. For tonight, I am content to transform that into a message that contains the X-Y coordinate at which the element was clicked. So I change the enteredView() method to:

I am especially interested in extending this idea to web services and sockets. If the trouble of setting those up can be hidden behind a web component, leaving only streams of events, I think there can be some really fun possibilities. Which I will start to explore tomorrow.

Wednesday, November 27, 2013

I am a little bummed that I was unable to get Polymer.dart tests working under Karma. It's not a huge deal because I do not need Karma's multi-browser runner—this is Dart code, so write once, run everywhere. Even though I am just testing my Dart code and relying on Dart itself to support multiple browsers, it still would have been nice.

For posterity, I believe that the main problem was dynamically adding the <link> imports of my custom Polymer elements:

URL must be imported by main Dart script: pricing_plans.dart
URL must be imported by main Dart script: pricing_plan.dart

Even if I try dynamically creating these in my test without Karma, I still see the same.

Since this is already being run inside an active Dart isolate, the import of those Polymer elements, with <script> tags of their own, is uncool. When those <link> tags exist before Dart spins up, they can be imported along with the backing Polymer classes (and no main() entry point) without conflicting with an existing VM isolate.

There are probably ways around this, but they involve too much reaching under the covers of Polymer.dart and/or Karma for my tastes at this point. All I want is to rerun my tests whenever I make a code change. Grunt ought to be able to do that.

I already have Grunt installed (npm install -g grunt with a recent Node.js), so my next step is to install the grunt-contrib-watch module (to watch for test and code changes) and the grunt-shell module (to run tests in response to those changes). I start with this package.json:

Most of that is pretty basic Grunt and grunt-contrib-watch configuration. At the bottom, I load my tasks (watch and shell) and register a new shell command that I will use to run my actual tests. Above, I watch for changes to Dart and HTML code in my test and lib directories. If there are any changes, then I invoke the shell:test task.

I will probably take some time to add exit code handling, but that seems like it will work quite nicely. I may not have Karma's ability to run the tests in multiple browsers, but with Dart, that is hardly a loss.

Tuesday, November 26, 2013

While writing the testing chapter in Patterns in Polymer, I found myself putting the Karma test runner section entirely inside a ifdef:js block of text. This got me to thinking. I really like Karma. I really like Dart. Can the two of them get along? Can Karma go in both the Dart and JavaScript versions of the book?

Well, there is one way to find out...

I already have a couple of tests written in Dart's unittest. There is a karma-dart testing framework definition. Maybe that'll just work. I start by installing the plugin via node.js's npm:

$ npm install karma-dart --save-dev

I start with the same karma.conf.js that I have been using for my JavaScript Polymer, but I change the framework (to dart-unittest) and the list of files to be used:

I am not sure about those two browser package dependencies, so I leave them commented out at first. But when I run Karma and attach Dartium to http://localhost:9876, I find that I need those after all:

...
Error: ENOENT, no such file or directory '/home/chris/repos/polymer-book/play/bootstrap-panels/dart/packages/js/js.dart'
at Object.fs.openSync (fs.js:427:18)
at Object.fs.readFileSync (fs.js:284:15)
...

Since I do not need these in my Polymer code, I add them as additional development dependencies in my Dart Pubpubspec.yaml:

name: pricing_panels
dependencies:
polymer: any
dev_dependencies:
unittest: any
browser: any
js: any

After a pub get, I restart my Karma server and try again in Dartium.

I no longer have errors about the js package. Now both of my tests are run and both fail with similar error messages:

Unfortunately, that does not work. I am still left with a distinct lack of shadow DOM on my custom Polymer elements. Furthermore, I am seeing Dartium errors like:

URL must be imported by main Dart script: pricing_plans.dart
URL must be imported by main Dart script: pricing_plan.dart

I am unsure what I might be doing that would cause Karma / unittest / Dartium to attempt to load these script files outside the context of my existing main() entry point that is running my test and Polymer.

Stumped, I call it a night. I may or may not revisit this. It is probably sufficient to Grunt a content_shell test run, which I may try tomorrow (hopefully with more luck).

Monday, November 25, 2013

I have a little work ahead of me as I figure out how to run large amounts of acceptance tests against the code in Patterns in Polymer. The test suites that exist around Polymer and Polymer.dart are exquisite, but tend to be of the unit test variety. That's great for API tests, but I need to write tests from the perspective of a human reader, not a program context. Hence the need to nail down acceptance testing.

I think I have a better handle on this in Dart than I do in JavaScript. Although testing Polymer.dart has proven to be different than acceptance testing in projects and in Dart for Hipsters, there are enough similarities that I feel like I ought to be OK. But before switching back, I would like to push past the first two tests that I wrote last night. That means that I need to be able to add and remove Polymer elements per-test (or test group).

When I was first exploring Polymer.dart a while back, I found that I could dynamically create Polymer elements with createElement(). That method appears to have since been removed (it was marked as temporary at the time). Without it, I might try to create my <pricing-plan> element in a setup block like so:

The aforementioned createElement() was good for getting around this problem with registered, custom elements. In the meantime, the ability to dynamically create these registered, custom elements has moved into dart:html in the form of Element.tag():

As far as I can tell, there is no easy way to insert raw custom tag HTML, which itself might contain other custom Polymer elements. At least not without falling back to “null tree sanitizers” to skip Dart's HTML sanitization:

I think I can live with that solution, at least for my tests. I suppose that, if I found myself dynamically generating large numbers of custom-element-containing HTML snippets from strings in actual application code that I could do something similar to this createElement(). Then again, that seems the very purpose of Polymer, so this may very well be a testing use-case only. Regardless, I think I have this sorted out well enough. Time for some writing...

But, unless I want to run a web server every time I run my tests, that /assets path will not work—not because of the leading, absolute path slash, but because assets requires a Dart pub web server or build. I am not fussed by this. It was not clear to me that these mostly-HTML files were assets or code. Now, I know.

After moving them into the lib directory along with the backing Dart code, the only thing remaining in asset is the Bootstrap CSS, which feels right:

Another thing that is going to cause me problems is Polymer.dart's need to import files itself. Since the test file is typically being loaded from a file:// URL, I need to restart Dartium with --allow-file-access-from-files so that I do not get cannot-load-file errors:

$ chrome --allow-file-access-from-files

As for the test files, I start with the web page context, which is a stripped-down version of the application page. In it, I need code that imports the Polymer definitions via <link> tags, a reference to my test code, and a faux-fixture <pricing-plan> tag:

As an avowed Dart homer, this probably will not come as much of a surprise, but this feels more stable than the JavaScript equivalent. It took me a while to sort out the HTML context and to realize that the normal Polyer.dart initialization was calling initPolymer(). But once I had those, writing the tests was easy. More importantly, write two tests was easy. Thanks to Dart's single point of entry, I didn't have to add any infrastructure in my test to ensure that Polymer was loaded or that it was not loaded twice. I needed only to reproduce the same application initialization in my test and I was good to go.

About the only complaint that I have is that getDistributedNodes() thing. C'mon Dart! I expected that to be just distributedNodes, not that JavaScript getter nonsense! I kid, I kid. Distributed nodes—where the stuff embedded in a template's <content> tag is rendered (i.e. distributed)—are a very new thing. I have no doubt that Dart will clean that up shortly, but if that is my biggest hardship, then I am in good shape testing-wise!

Saturday, November 23, 2013

I still have more questions than I'd like in my JavaScript implementation of a Polymer + Boostrap element. But I think most of those questions can be deferred until later. Tonight I convert my custom Polymer elements into Dart using Polymer.dart.

I am unsure what the code organization ought to be, so I am going to guess tonight and circle back around later to see if any changes are needed. Loosely following Dart Pub guidelines, I create an asset directory to hold my Polymer.dart HTML templates and my bootstrap CSS, a lib directory for my Polymer.dart code, and a web directory for my sample application web page.

Next, I create the usual pubspec.yaml listing only Polymer as a library (non-development) dependency:

The stuff that I placed in the asset directory is, by Pub convention, accessible from the /assets/<package name>/ URL space, which is where the Polymer element definitions and Boostrap CSS are coming from. Also in there is the normal Polymer.dart initialization code. Well, the new normal at least as this has changed slightly since the last time I played with this project.

Taking a closer look at the Polymer element definition, not much needs to change from the JavaScript version:

In fact, the only thing that has changed is the <script> tag, which, naturally enough, now points to Dart code. Since the Dart code resides in the lib directory, the URL is slightly different than the asset HTML URL.

What is interesting is that I did not need to tell my JavaScript version to do the same thing. I am unsure if this is due to different Chrome versions (31 for Dart, 32 for JavaScript) or the embedded Dart VM in the former. The “author styles” thing seems to be a real thing, so I would expect that it is needed in the JavaScript version as well. Ah well, grist for another day.

For now, I have made the successful transition with a minimum of pain. Up tomorrow, I will explore testing of Polymer in Dart. Hopefully thanks to Dart's excellent testing and my hard-earned knowledge of Polymer testing, this will go a little smoother than when I was earning that knowledge.

Friday, November 22, 2013

Testing my fairly simple Polymer elements has been an unexpected adventure. But I think that I may have cracked it.

Although last night's excursion into testing the test Shadow DOM will no doubt be of use at some point, it feels more like testing the framework than serving to test something of real value. I think that the tests that I wrote previously capture that spirit. And, as I found when I tried my second test, I suspect that adding a second specification to the same test suite will teach me.

I continue to test a couple of Boostrap web components: <pricing-plans> and its child <pricing-plan. The idea is to convert the mess of <div> tags that usually build something like:

With Polymer web components, these can be expressed as the much more readable:

I might quibble over the difficulty in distinguishing between the parent and child tag names, but that aside, this is more more readable than slogging through a bunch of <div class="col-md-10 col-sm-4"> tags in order to find my actual content.

What I have come to understand as I have explored testing the child <pricing-plan> tags is that the hardest thing to get right is the way that Polymer wants to be loaded—first:

In the spec for the <pricing-plan> tag, I had been doing that with a “before all” test setup block. Before all specs, I dynamically inserted the Polymer library in the test page's <head> so that it would be loaded before anything else. And that worked.

But it raised the question: what happens if there is a second specification file with its own “before all”? Should the second “before all” detect the presence of the Polymer library from the first? Should both detect Polymer so that their order can be changed? Bother that.

Instead, I think I can lean on my testing framework (Jasmine) and my test runner (Karma) to fix this. I start with the later, where I make the first included file a new PolymerSetup.js:

UPDATE In order to ensure that Polymer was ready, I found that I needed a Jasmine beforeEach() back in the PolymerSetup.js that waits for CustomElements to be ready (the same as waiting for the WebComponentsReady event to fire):

Thursday, November 21, 2013

After last night, I have a setup for testing Polymer elements. I don't know if it is a good setup, let along the setup. Luckily, I have a reasonably involved pair of Polymer elements to try things out with.

As I found yesterday, because of the way that Polymer likes to establish itself and ready itself to do its web component thing, the setup in Jasmine tests was a little awkward. In application code, the <head> of the HTML document needs to look like:

There is not really a “before all” built into Jasmine which is why I added in the ugly previouslyLoaded conditional.

Anyhow, what I would like to test tonight is that the content that I wrap with my Polymer element winds up being rendered in the proper location. This turns out to be much harder than I expected. The fixture that I am placing into my document.body is pretty simple:

<pricing-plan>pricing plan 01</pricing-plan>

I just need to verify that the string "pricing plan 01" is embedded inside of the Polymer element. If I inspect the element on the debug page, it is definitely there:

So how hard can it be to query programatically? Well, it involves something called distributed nodes. It seems that the text content from my fixture is still in the original element. If I query the element's text content from the JavaScript, I see it:

And to test for that, I need to go through my Polymer element's shadow DOM. The >content> element resides there in the "shadows" of the real DOM element, my custom <pricing-plan> element. Once I have that, use the getDistributedNode() method to get the first node that was distributed. Hopefully that is my text snippet:

Wednesday, November 20, 2013

Last night, I was able to get the Karma test runner to test a Polymer element. I think.

Honestly, I have good reason to think that it worked: the debug page in Chrome included the Shadow DOM. But I did not actually test any of the Polymer code or resulting display, so tonight I make sure that I have this working.

By way of review, I setup Karma with mostly default values (including using the Jasmine testing framework). The only changes were to preprocess and include test/*.html testing templates and to serve but not include the Polymer files in the testing context page. The relevant section of the configuration:

/**
* Compile HTML into JS so that they can be used as templates
*/
preprocessors: {
'test/*.html': 'html2js'
},
/**
* Don't include Polymer HTML and JS because Polymer is very
* particular about the order in which they are added. Serve them,
* but defer loading to the test setup. Include test HTML
* fixtures.
*/
// list of files / patterns to load in the browser
files: [
{pattern: 'scripts/**/*.html', included: false},
{pattern: 'scripts/**/*.js', included: false},
'test/**/*Spec.js',
'test/*.html'
],

The Polymer project is picky about when Polymer.js needs to be loaded. Well, not too picky—just before code that touches the DOM:

And, as I mentioned at the outset, that seems to work. Karma's debug test page has the results that I would expect. But I am only testing that the <pricing-plan> tag, which is defined in the testing template, exists:

That test would pass with or without Polymer running properly (and it did last night, which caused me more than a little confusion). What I really want to know about this particular polymer element is: does its shadow DOM include all of the Bootstrap <div> tags that I am trying to hide?

And that's where everything falls apart. Of course, it's always the second test that breaks things...

If I add a test that looks for the default pricing plan's ("Plan") in the shadow DOM of my custom element, it passes. But only if I comment out the original test:

After a little investigation, I surmise that this is due to the beforeEach() setup which adds the Polymer script file multiple times. And this seems to be the case as I can get both tests passing with a conditional that loads the Polymer files only once:

Two other things that I note here are that the document.body.innerHTML replacement also needs to be done only once. If that is brought outside the conditional, the second test will fail. Similarly, the waits(100) at the end of the setup also seems to be necessary to give Polymer adequate time to get ready to do its thing.

This does not feel terribly robust. What happens, for instance, I try to test multiple tags with different attributes or dynamically added custom elements? What if I need to test multiple Polymer elements that work together? Will that setup stand up or will I see the return of strange errors? Also, I feel as though I am likely missing a Polymer callback or two with the need for waits(100). Hopefully I can answer those questions and make some improvements in the days to come.

Tuesday, November 19, 2013

The end of Patterns in Polymer will deal with testing and maintainability. I can't leave research on testing until the book is nearly done. In fact, it is rather important that I nail it down very early so that I have assurance that the code referenced throughout the book works - especially as the Polymer project continues to evolve.

I find it interesting that my posts on testing tend to have fewer readers. I suppose I understand in a way why this is. It is always fun to play and create with new tools. I like it too. But a tool that does not support strong testing practices is a tool that promotes codebases that will rot. So I quickly move from introduction to testing. If I am not satisfied, it is time to drop the new shininess for something better.

Testing is one of the reasons I love Dart so much. I have already written some extensive tests in Polymer.dart, so I am quite comfortable with that aspect of testing in Patterns in Polymer. But testing the JavaScript version? No idea.

The only mention of testing on the project page discusses building Polymer itself, not a project. Searches currently return very little, so it seems that I am on my own for now. In JavaScript land, I have been defaulting to Karma and Jasmine.

I already have Karma installed on my system:

➜ bootstrap-panels git:(master) karma --version
Karma version: 0.10.2

If I did not, installing it is a simple matter of installing Node.js and then npm install -g karma.

There is a lot to love about Karma, but getting started may at the top of the list. The built-in karma init is wonderful:

➜ bootstrap-panels git:(master) karma init
Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.
> jasmine
Do you want to use Require.js ?
This will add Require.js plugin.
Press tab to list possible options. Enter to move to the next question.
> no
Do you want to capture a browser automatically ?
Press tab to list possible options. Enter empty string to move to the next question.
> Chrome
>
What is the location of your source and test files ?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Enter empty string to move to the next question.
> test/**/*Spec.js
>
Should any of the files included by the previous patterns be excluded ?
You can use glob patterns, eg. "**/*.swp".
Enter empty string to move to the next question.
>
Do you want Karma to watch all the files and run the tests on change ?
Press tab to list possible options.
> yes
Config file generated at "/home/chris/repos/polymer-book/play/bootstrap-panels/karma.conf.js".

Normally I would also include PhantomJS in the list of browsers for better continuous integration testing support, but tonight I just want to smoke test my testing. Chrome should be sufficient for that.

I need two changes to the default values that I wrote to karma.conf.js. First, I need to include the Polymer JavaScript source into the Jasmine context.html page. This is done by adding 'scripts/**/*.js' to the list of files section the configuration. The other change that I make is to load fixture data. I am not a huge fan of fixture files for JavaScript testing, but I am even less a fan of string manipulation in JavaScript, so I use fixtures. For these, I need to include them in the list of files and add an html2js preprocessor:

That is not quite success, however. Polymer takes some time to load and my test is not waiting for that before running the tests. I am only verifying that the bare tag exists, not that the Polymer element is doing anything. I think that is going to be a more significant undertaking, possible involving requirejs to ensure script load order (Polymer really needs to come first). I will pick back up with that tomorrow.

UPDATE: I was able to get the Polymer code firing with some addition beforeEach() setup:

Monday, November 18, 2013

I will be the first to admit that I have no idea if it is a good idea to couple Bootstrap and Polymer like I have been doing over the past few days. It mostly just scratched an annoying itch that I got from putting together the Patters in Polymer landing page. The result has been quite pleasant and I think that I have the opportunity to take it a step further.

Instead of the usual crazy amount of Bootstrap <div>s, I have reduced my HTML to a very clean:

Which, thanks to the Polymer definition of <pricing-plan> results in Bootstrap's nice panels:

Really, the only thing still cluttering up the pricing plan HTML is the container code. If I could reduce that to simply <pricing-plans>, life would be much simpler. It does not hurt that this would be my first attempt a two interacting Polymer elements.

Actually, this is easy, right? I need another Polymer element whose template is those Bootstrap <div>s wrapping a <content></content> declaration. In Polymer, <content> is like a Ruby yield - it yields whatever the container is wrapping. So I do the usual, starting with a new <link> tag in the main page:

The only thing making this anything other than stock Polymer is the dynamic inclusion of the Bootstrap CSS (which was part of the <pricing-plan> definition last night. With that, the HTML in my main document becomes:

The last thing that I would like to do it automatically resize the panels depending on how many of them there are. I do not know if it is possible for the parent element, <pricing-plans> to communicate to the child objects. I am not registering them in any way, so I would tend to think not. So instead, I teach the individual <pricing-plan> elements how to do this.

Back in the pricing-plan.html definition, I add a ready() callback method to the element's definition:

And that actually works! If I remove one of the plans, leaving two, the remaining plans now occupy half of the normal Boostrap 12 columns.

Nice. It is really impressive how little code is required to realize some significant HTML clean-up. I am also enjoying how nice it is to work with the "easy" stuff. I still might like to investigate if actual communication is possible between elements, rather than just probing the environment. But, for now, I will happily take this little win.

Aside from the containing col-md-10 (which I will worry about tomorrow), that is a very pleasant and, dare I say it, semantic bunch of content.

I left off last night wanting to be able to adjust the panel type and size. Both of those are fairly trivial modifications to the pricing-plan.html polymer-element definition that is referenced by my page:

Darn it. That was a little too easy. Darn you Polymer, I normally like to make things a little harder for myself. So I ask a bonus question tonight. How can I do this without requiring the web page to load the Bootstrap CSS and JavaScript? The answer, at least as far as I can tell from the FAQ is to add tags such that the browser can ignore dependedencies that are required multiple times.

Even if I add that <link> tag a hundred times, the browser should only make a single request for the duplicate resource. And, if I clear the browser cache and reload the page, that is exactly what I see:

Saturday, November 16, 2013

Tonight, I step a little outside my comfort zone and program in... JavaScript.

Well, OK, not too far outside my comfort zone, but since I turned 3D Game Programming for Kids over to my editor, it has been a good streak of pure Dart blogging. But, for my next book to come in Dart and JavaScript flavors, I necessarily need to have a good feel for coding Polymer in JavaScript. Since I have yet to do that, now seems a good time to start.

I like to start simple and dumb, but never seem to manage it. One could argue that those are the same things, but I manage to do both quite often, thank you very much! By "simple," I mean limiting newish features and coupling. By "dumb," I mean that I am probably going to be misusing Polymer. I think the <ice-code-editor> tag that I created in Dart was pretty cool, but it was pretty complicated given the underlying editor. Tonight, I start with <pricing-plans>, which will not be simple because of external coupling.

I wound up spending a lot of time on the pricing plans section of the Patterns in Polymer landing page. Since I have other books that I might like to sell in a similar fashion, maybe the <pricing-plans> tag isn't too dumb. But it probably is...

The thing that was giving me grief was fiddling with various Bootstrap panel class values:

After a while, that stuff starts to get to me. Since this tends to be very repetitive, perhaps this is something that I can extract out? I have no real idea if it is a good idea to couple Bootstrap and Polymer, but there is one way to find out!

So I do the usual Polymer thing by including the library and a reference to the element that I am going to create in the <head> section of my HTML document:

I have pulled in the repetitive panel definition, but not the actual list of features. Those are specific to each panel, so I will need to include that content where the <content> tag is in my template. Back in the main document, I can use this tag as:

That is a lot cleaner and easier to follow. Especially given that I did not have too much work to make it happen:

The last thing that I would like to do tonight is the ability to specify the name of the pricing plan. For this one, it would be "Multi-language." For that, I need to drop the noscript declaration from my <polymer-element> definition. In its place, I need a very simple script that creates a simple Polymer object:

<script>
Polymer('pricing-plan');
</script>

That allows me to declare a list of public attributes for my element. The only attribute that I need is name so my entire <polymer-element> becomes:

I still might like the ability to make those columns a little wider or more narrow, depending on how many pricing plans I have. Ooh! And I still need to be able specify one as being the primary entry. Grist for tomorrow.