Monday, November 18, 2013

Working with Multiple Polymer Elements


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:
<div id="price">
  <div class="container">
    <div class="col-md-10 col-sm-4">
    <!-- pricing plans here... -->
      <pricing-plan name="Extras" type="primary">
        <ul>
          <li>Get the multi-language pack plus…</li>
          <li><strong>Screencast</strong> of <span title="Test Driven
            Development">TDDing</span> a polymer element.</li>
          <li>The <strong>warm feeling</strong> of helping
            produce the book.</li>
          <li>Inclusion in the <strong>supporters</strong> section of the
            book.</li>
          <li>Chris's undying <strong>love</strong>.</li>
          <li>More...?</li>
        </ul>
      </pricing-plan>
    </div> <!-- class="col-md-10 col-md-offset-1 col-sm-4 " -->
  </div> <!-- /.container -->
</div> <!-- /#price -->
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:
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Test</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="scripts/polymer.min.js"></script>
    <link rel="import" href="pricing-plans.html">
    <link rel="import" href="pricing-plan.html">
  </head>
  <body>
    <!-- HTML, including Polymer elements, here... -->
  </body>
</html>
The simple <content> wrapping pricing-plans.html is then:
<polymer-element name="pricing-plans">
  <template>
    <div id="price">
      <div class="container">
        <div class="col-md-10 col-sm-4">
          <content></content>
        </div>
      </div>
    </div>
  </template>
  <script>
    var ss = document.createElement("link");
    ss.type = "text/css";
    ss.rel = "stylesheet";
    ss.href = "css/bootstrap.min.css";
    document.getElementsByTagName("head")[0].appendChild(ss);

    Polymer('pricing-plans');
  </script>
</polymer-element>
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:
<pricing-plans>
  <pricing-plan name="Multi-Language">
    <ul><!-- ... --></ul>
  </pricing-plan>
  <pricing-plan name="I Only Like One">
    <ul><!-- ... --></ul>
  </pricing-plan>
  <pricing-plan name="Extras" type="primary">
    <ul>
      <li>Get the multi-language pack plus…</li>
      <li><strong>Screencast</strong> of <span title="Test Driven
        Development">TDDing</span> a polymer element.</li>
      <li>The <strong>warm feeling</strong> of helping
        produce the book.</li>
      <li>Inclusion in the <strong>supporters</strong> section of the
        book.</li>
      <li>Chris's undying <strong>love</strong>.</li>
      <li>More...?</li>
    </ul>
  </pricing-plan>
</pricing-plans>
Pretty!

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:
<polymer-element name="pricing-plan" attributes="name type size">
  <template><!-- ... --></template>
  <script>
    Polymer('pricing-plan', {
      name: 'Plan',
      type: 'default',
      size: 1,
      ready: function() {
        this.size = 12 / this.parentElement.childElementCount;
      }
    });
  </script>
</polymer-element>
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.


Day #939

3 comments:

  1. I think there might be some trouble with Bootstrap CSS rules that would need to cross the boundaries between parent and child element like

    .navbar-default .navbar-nav>li>a

    What do you think?

    ReplyDelete
    Replies
    1. Agreed. Even in this simple solution, there is some coupling between parent and child elements representing different Boostrap levels - it just didn't end up affecting things this time. You're probably correct that the coupling would be problematic in some cases. It might be possible for the parent to dynamically add CSS rules in those cases, but I'd just be guessing.

      Maybe a topic for another night :)

      Delete
  2. We are developing a huge project with Google Polymer combine with Bootstrap. We had applied many trick to ... hack ... so that CSS affect would work.
    By the way, although Google Polymer is still in development phase and has a lot of bug, many features do not work on IE, firefox, safari... but it has proven to improve the productivity on the whole. Especially, it reduce conflicting of the javascript event binding on element within the rendered page.

    ReplyDelete