Search code examples
javascriptpolymerweb-componentpolymer-1.0

How to pass unknown children of Polymer component into component's DOM


I want to create a component which I can pass other components to and create DOM structures.

<test-component>
    <img/>
    <div/>
    <foo/>
</test-component>

In trying to get that working:

<dom-module name="test-component">
    <template>
        <h1>Title</h1>
        <ul>
            <template is="dom-repeat" items="{{nodes}}">
                <li>element:</li>
                <li>{{item}}</li>
            </template>
        </ul>

        <content id="contentelement"></content>
    </template>
</dom-module>

<script type="text/javascript">
    Polymer({
        is: "test-component",
        ready: function() {
            this.nodes = (function(that) {
                var nodeList = that.$.contentelement._distributedNodes,
                    nodeMap = [];
                for(var i = 0; i < nodeList.length; i++) {
                    if(nodeList[i].nodeType === 1) {
                        nodeMap.push(nodeList[i].outerHTML);
                    }
                }
                return nodeMap
            }(this));
        }
    });
</script>

I used a function to build this.nodes because this.nodes = this.$.contentelement.getDistributedNodes(); returns null, not sure why.

I know you can't just drop an element's outerHTML into the page but can a random set of HTML/components be passed into a template like this?


Solution

  • Unfortunately this is currently not possible with data-binding (have a look at this discussion on Github).

    Here's a possible workaround in JavaScript.

    <dom-module name="test-component">
        <template>
            <h1>Title</h1>
            <ul>
                <template is="dom-repeat" items="{{nodes}}">
                    <li>element:</li>
                    <li class="content"></li>
                </template>
            </ul>
            <content id="contentelement"></content>
        </template>
    </dom-module>
    
    <script type="text/javascript">
        Polymer({
            is: "test-component",
            ready: function() {
                this.nodes = this.getContentChildren();
                this.async(function(){
                    var contentNodes = Polymer.dom(this.root).querySelectorAll(".content");
                    this.nodes.forEach(function(element, index) {
                        contentNodes[index].innerHTML = element.outerHTML;
                    });
                }.bind(this));
            }
        });
    </script>
    

    Edit

    You can remove the content nodes from their original location after you have accessed them by removing them from the DOM.

    this.nodes = this.getContentChildren();
    Polymer.dom(this.root).removeChild(this.$.contentelement);