Server-side components in Polymer2 template

You can add child components to templates using the Component or Element API, but because PolymerTemplate uses the shadow DOM the shadow tree is rendered instead of the elements children that are in the light DOM.

This means that the template needs to have a <slot></slot> to mark the place where the light DOM elements should be rendered.

For example you could have the html template built something like this:

<link rel="import" href="/bower_components/polymer/polymer-element.html">

<dom-module id="component-container">
    <template>
        <div>
            <slot></slot>
        </div>
    </template>

    <script>
        class ComponentContainer extends Polymer.Element {
            static get is() {
                return 'component-container'
            }
        }
        customElements.define(ComponentContainer.is, ComponentContainer);
    </script>
</dom-module>

and the server-side template as:

@Tag("component-container")
@HtmlImport("/com/example/ComponentContainer.html")
public class ComponentContainer extends PolymerTemplate<TemplateModel> {

    public ComponentContainer() {
        Element label = ElementFactory.createLabel("Main layout header");
        Element button = ElementFactory.createButton("Click me");

        getElement().appendChild(label, button);
    }
}

Without the slot tag in the template the added label and button would not be visible to the user even though it can be located in the DOM. As we can see multiple components will show up in the slot when added to the template element that has an open <slot></slot>

Note
Any element added to the light dom to be shown inside a <slot> can be removed from the dom and it will work as expected and disappear from the main element.

Default content slots and named slots in templates

The <slot> tag can contain "default" content if nothing is set from the light dom.

For example the template could be built as:

    <template>
        <div style="border: 1px solid black; padding: 10px; margin: 10px;">
            <slot>No components added</slot>
        </div>
    </template>

This would show at start 'No components added' in the slot which would then be replaced by the first added child element.

Slots can also be named so that only wanted content is added there. This is done by using the name attribute <slot name="{name-here}"> for instance:

    <template>
        <h1><slot name="title"></slot></h1>

        <div style="border: 1px solid black; margin: 5px;padding: 5px;">
            <slot>No content given!</slot>
        </div>
    </template>

Now an element that could be for instance: <label slot="title">Main header</label> would not be positioned in the slot with the default "No content given!" but into the <h1><slot name="title">…​

Named slots can also be used as default by nesting them inside the main slot like:

    <template>
        <slot name="fullName">
            <slot name="firstName"></slot>, <slot name="lastName"></slot>
        </slot>
    </template>

This will make the slot show data for light dom slot="firstName" slot="lastName" elements if no slot="fullName" is available and when a <element slot="fullName"> is added it will "override/replace" the firstName, lastName data.

The "default" slot and named slot can contain multiple elements.

@Tag("name-element")
@HtmlImport("/com/example/NameElement.html")
public class NameElement extends PolymerTemplate<TemplateModel> {
    public NameElement() {
        Element firstName = ElementFactory.createSpan("Jack");
        Element middleName = ElementFactory.createSpan(" James");
        Element surName = ElementFactory.createSpan("Christobald");

        firstName.setAttribute("slot", "firstName");
        middleName.setAttribute("slot", "firstName");
        surName.setAttribute("slot", "lastName");

        getElement().appendChild(firstName, middleName, surName);
    }
}

Using a PolymerTemplate as a Parent Layout

For general info about parent views, see Router Layouts and Nested Router Targets. For general information about templates, see Creating A Simple Component Using the Template API.

A PolymerTemplate class can be used as a parent layout by using the <slot></slot> in the position where the child view should be displayed.

To define a parent layout that shows the actual view below a heading a and a menu, MainLayout.html could look like this:

<link rel="import" href="/bower_components/polymer/polymer-element.html">

<dom-module id="main-layout">
    <template>
        <h1>Site title</h1>
        <div class="menu">...</div>

        <!-- child content comes here -->
        <slot></slot>
    </template>

    <script>
        class MainLayout extends Polymer.Element {
            static get is() {
                return 'main-layout'
            }
        }
        customElements.define(MainLayout.is, MainLayout);
    </script>
</dom-module>

To use the template file, you also need a basic template component class with an html import for the template file. Right now you need to also implement the RouterLayout interface:

@Tag("main-layout")
@HtmlImport("/com/example/ComponentContainer.html")
public class MainLayout extends PolymerTemplate<TemplateModel>
        implements RouterLayout {
}
Note
The method showRouterLayoutContent(HasElement) in the RouterLayout interface has default implementation so you don’t need to write anything in addition to that. But you may reimplement it as you wish.

Now you may use MainLayout as a parent layout via @Route annotation or @ParentLayout annotation:

@Route(value="editor", layout=MainLayout.class)
public class Editor extends Div {
}

@ParentLayout(MainLayout.class)
public class MenuBar extends Div {
}
Note
See Router Layouts and Nested Router Targets for the details about using routing and the annotations mentioned here.