Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to inject template using slot with data-binding in Polymer2

I'd like to inject a rendering template from a parent component to a child component using <slot> insertion points. The injected template contains data-binding on a property of the child component (my-child.data in this case).

<dom-module id="my-parent">
  <template>
    <my-child>
      <template>
        <div>Child's data property: [[data]]</div>
      </template>
    </my-child>
  </template>
  ...

The rendering child component basically looks like this:

<dom-module id="my-child">
  <template>
    <header></header>
    <slot></slot>
    <footer></footer>
  </template>

  <script>
    class MyChild extends Polymer.Element {
      static get is() { return 'my-child'; }
      static get properties() {
        return {
          data: { ... }
        };
      }
      ...

I'm not sure whether this is possible at all with Polymer2. Vue2 has a concept called "scoped slot" to achieve this. Thanks in advance for any feedback!

like image 766
Oliver Hader Avatar asked Jun 07 '17 12:06

Oliver Hader


1 Answers

Data binding is by default tied within the current scope of the binding. If you wish to change the scope, you must put your markup inside a <template> tag and stamp in it inside a different scope.

Your HTML code in the question is already OK - you actually wrap the light DOM inside a <template>, but you then use that <template> incorrectly. You must not use <slot>, but must stamp that template manually and insert it somewhere inside the my-child element's shadow DOM.

Here you have a working demo on how to achieve this: http://jsbin.com/loqecucaga/1/edit?html,console,output

I have even added the data property binding to an input element in order to demonstrate that property changes also affect the stamped template.

The stamping is relatively simple and is done inside the connectedCallback method:

var template = this.querySelector('template');
this.__instance = this._stampTemplate(template);
this.$.content.appendChild(this.__instance);

The stamped template is put inside a placeholder div element, which you put somewhere inside the my-child's template:

<div id="content"></div>

To sum up, here is the full code from the demo:

<link href="polymer/polymer-element.html" rel="import"/>
<link href="polymer/lib/mixins/template-stamp.html" rel="import"/>

<dom-module id="my-parent">
  <template>
    <my-child>
      <template>
        <div>Child's data property: [[data]]</div>
      </template>
    </my-child>
  </template>

  <script>
    class MyParent extends Polymer.Element {
      static get is() { return 'my-parent'; }
    }

    window.customElements.define(MyParent.is, MyParent);
  </script>
</dom-module>

<dom-module id="my-child">
  <template>
    <header>Header</header>
    <div id="content"></div>
    <footer>Footer</footer>
    <input type="text" value="{{data::input}}" />
  </template>

  <script>
    class MyChild extends Polymer.TemplateStamp(Polymer.Element) {
      static get is() { return 'my-child'; }
      static get properties() {
        return {
          data: {
            type: String,
            value: 'Hello, World!'
          },
        };
      }

      connectedCallback() {
        super.connectedCallback();

        var template = this.querySelector('template');
        this.__instance = this._stampTemplate(template);
        this.$.content.appendChild(this.__instance);
      }
    }

    window.customElements.define(MyChild.is, MyChild);
  </script>
</dom-module>

<my-parent></my-parent>
like image 148
alesc Avatar answered Oct 01 '22 12:10

alesc