aloo
aloo

Reputation: 5389

how to bind polymer custom element to the contents?

I'm trying to create an element which has a dynamic number of tabs depending on what content is given to the element. The only way I've figured out how to do this is in JS but I have a feeling this isn't the Polymer way, what am I missing? Can you point me to some docs or other elements that will help me get started?

here's what I have so far:

Using my custom element

<code-file-set>
   <a href="#" tabTitle="onelink">onelink</a>
   <a href="#" tabTitle="twolink">twolink</a>
   <a href="#" tabTitle="threelink">threelink</a>
</code-file-set>

Implementation of my custom element

<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="/bower_components/core-pages/core-pages.html">
<link rel="import" href="/bower_components/paper-tabs/paper-tabs.html">

<polymer-element name="code-file-set">
  <template>

      <paper-tabs id="tabs" selected={{selected}}>
      </paper-tabs>

      <core-pages selected="{{selected}}">
        <content id="codecontent" select="a"></content>
      </core-pages>
  </template>

<script>

  Polymer('code-file-set', {
    domReady: function() {
      var codeNodes = this.$.codecontent.getDistributedNodes();

      for (var i = 0; i < codeNodes.length; i++) {
        var name = codeNodes[i].getAttribute('tabTitle');
        var tab = document.createElement('paper-tab');
        tab.innerHTML = name;
        this.$.tabs.appendChild(tab);
      }    
      this.selected = 0;
    },
  });
</script>
</polymer-element>

This works, but it seems ugly. Namely, can creating the tabs not be done in JS but in a more declarative way in the HTML (notice that I create one tab per element found in the content).

Upvotes: 1

Views: 1119

Answers (2)

robdodson
robdodson

Reputation: 6786

I wanted to add an answer since even though one has already been accepted because you're not limited to using data binding. There are many cases where it may be preferable to use the content. It's a little bit more work but it will allow you to compose other elements inside of it if you so choose.

If you use a <content> element you can get at the distributed children using getDistributedNodes(). If you add a select attribute to your <content> element, it will ignore text nodes and create a NodeList of only the elements you want. ex: <content select="a">. Then you create an internal variable to point at those children, and use template repeat with that variable.

Code example:

<polymer-element name="x-menu">
  <template>
    <style>
      ::content > * {
        display: none;
      }
    </style>
    <content id="content" select="a"></content>
    <paper-tabs link>
      <template repeat="{{tab in tabs}}">
        <paper-tab>
          <a href="{{tab.href}}" horizontal center-center layout> 
            {{tab.textContent}}
          </a>
        </paper-tab>        
      </template>
    </paper-tabs>
  </template>
  <script>
    Polymer({
      domReady: function() {
        this.tabs = this.$.content.getDistributedNodes().array();
      }
    });
  </script>
</polymer-element>

<x-menu>
  <a href="#foo">Foo</a>
  <a href="#bar">Bar</a>
  <a href="#baz">Baz</a>
</x-menu>

example jsbin: http://jsbin.com/duzapu/6/edit

One thing to note, if you're expecting the number of children to change at any point, you should watch for that using onMutation and update your internal variable accordingly.

Upvotes: 3

Justin XL
Justin XL

Reputation: 39006

A proper data binding solution would be to define an array (tabs) as a published property and use template repeat to iterate through the array and populate each item onto the UI.

<polymer-element name="code-file-set" attributes="tabs">
    <template>
        <paper-tabs style="background-color:limegreen" selected="0">
            <template repeat="{{tab in tabs}}">
                <paper-tab>
                    <a href="{{tab.link}}">{{tab.tabTitle}}</a>
                </paper-tab>
            </template>
        </paper-tabs>
    </template>

    <script>
        Polymer('code-file-set', {
            ready: function () {
                this.tabs = [];

                for (var i = 0; i <= 3; i++) {
                    this.tabs.push({ link: '#', tabTitle: 'title ' + i });
                }
            },
        });
    </script>
</polymer-element>

See this jsbin as a working example.

Upvotes: 2

Related Questions