Polymer makes it simple to create web components, declaratively.
New web developers can simply add custom HTML elements on a web page with markdown. It’s just like using the HTML tags you’re already familiar with:
<h1>A heading!</h1>
<fancy-thing>A fancy thing!</fancy-thing>
Experienced web developers can use Polymer's special features to reduce boilerplate and make it even easier to build complex, interactive elements. In this tour, you'll learn how to:
- Register elements
- Use lifecycle callbacks
- Observe properties
- Create shadow DOM with templates
- Use data binding
In this section you can tour the Polymer library, without installing anything. Click the Edit on Plunker button to open any of the samples in an interactive sandbox.
Tap the buttons following each feature to learn more.
Register an element
To register a new element, create an ES6 class that extends
Polymer.Element
, then call the customElements.define
method, which
registers a new element with the browser. Registering an element associates
an element name with a class, so you can add properties and methods to your custom
element. The custom element's name must start with an ASCII letter and
contain a dash (-).
<link rel="import" href="https://polygit.org/components/polymer/polymer-element.html">
<script>
// Define the class for a new element called custom-element
class CustomElement extends Polymer.Element {
static get is() { return "custom-element"; }
constructor() {
super();
this.textContent = "I'm a custom-element.";
}
}
// Register the new element with the browser
customElements.define(CustomElement.is, CustomElement);
</script>
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://polygit.org/components/webcomponentsjs/webcomponents-loader.js"></script>
<link rel="import" href="custom-element.html">
</head>
<body>
<custom-element></custom-element>
</body>
</html>
Try it out in Plunker:
- Try modifying the contents of
this.textContent
. - If you’re familiar with your browser’s developer tools, try printing the
custom element’s
tagName
property to the console. Hint: addconsole.log(this.tagName);
to the constructor method!
This sample uses a lifecycle callback
to add contents to the <custom-element>
when it's initialized.
When a custom element finishes its initialization, the ready
lifecycle callback is called.
You can use the ready
callback for one-time initialization work after the element is created.
Learn more: element registration
Learn more: lifecycle callbacks
Add shadow DOM
Many elements include some internal DOM nodes to implement the element's UI and behavior. You can use Polymer's DOM templating to create a shadow DOM tree for your element.
<link rel="import" href="https://polygit.org/components/polymer/polymer-element.html">
<dom-module id="dom-element">
<template>
<p>I'm a DOM element. This is my shadow DOM!</p>
</template>
<script>
class DomElement extends Polymer.Element {
static get is() { return "dom-element"; }
}
customElements.define(DomElement.is, DomElement);
</script>
</dom-module>
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://polygit.org/components/webcomponentsjs/webcomponents-loader.js"></script>
<link rel="import" href="dom-element.html">
</head>
<body>
<dom-element></dom-element>
</body>
</html>
Try it out in Plunker:
- Try adding some other html elements inside the block. For example, add
<h1>A heading!</h1>
or<a href=”stuff.html”>A link!</a>
Shadow DOM is encapsulated inside the element.
Compose with shadow DOM
Shadow DOM lets you control composition. The element's children can be distributed so they render as if they were inserted into the shadow DOM tree.
This example creates a simple tag that decorates an image by wrapping it
with a styled <div>
tag.
<link rel="import" href="https://polygit.org/components/polymer/polymer-element.html">
<dom-module id="picture-frame">
<template>
<!-- scoped CSS for this element -->
<style>
div {
display: inline-block;
background-color: #ccc;
border-radius: 8px;
padding: 4px;
}
</style>
<div>
<!-- any children are rendered here -->
<slot></slot>
</div>
</template>
<script>
class PictureFrame extends Polymer.Element {
static get is() { return "picture-frame"; }
}
customElements.define(PictureFrame.is, PictureFrame);
</script>
</dom-module>
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://polygit.org/components/webcomponentsjs/webcomponents-loader.js"></script>
<link rel="import" href="picture-frame.html">
</head>
<body>
<picture-frame>
<img src="https://www.polymer-project.org/images/logos/p-logo-32.png">
</picture-frame>
</body>
</html>
Try it out in Plunker:
- Try adding a
<div>
toindex.html
; is it affected by the styles in<picture-frame>
's shadow DOM? - Try adding other HTML elements to the DOM template to see how they are positioned relative to the distributed child nodes.
Note: The CSS styles defined inside the <dom-module>
are scoped to the element's shadow DOM.
So the div
rule here only affects <div>
tags inside <picture-frame>
.
Learn more: Composition & distribution
Use data binding
Of course, it's not enough to have static shadow DOM. You usually want to have your element update its shadow DOM dynamically.
Data binding is a great way to quickly propagate changes in your element and reduce boilerplate code.
You can bind properties in your component using the "double-mustache" syntax ({{}}
).
The {{}}
is replaced by the value of the property referenced between the brackets.
<!-- import polymer-element -->
<link rel="import" href="https://polygit.org/components/polymer/polymer-element.html">
<dom-module id="name-tag">
<template>
<!-- bind to the "owner" property -->
This is <b>{{owner}}</b>'s name-tag element.
</template>
<script>
class NameTag extends Polymer.Element {
static get is() { return "name-tag"; }
// set this element's owner property
constructor() {
super();
this.owner = "Daniel";
}
}
customElements.define(NameTag.is, NameTag);
</script>
</dom-module>
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://polygit.org/components/webcomponentsjs/webcomponents-loader.js"></script>
<link rel="import" href="name-tag.html">
</head>
<body>
<name-tag></name-tag>
</body>
</html>
Try it out in Plunker:
- Try editing the value of the
owner
property. - Try adding another property and binding it in your component.
Hint: Add
this.propertyName = "Property contents";
to the constructor and add {{propertyName}} to the element’s shadow DOM.
Declare a property
Properties are an important part of an element's public API. Polymer declared properties support a number of common patterns for properties—setting default values, configuring properties from markup, observing property changes, and more.
The following example declares the owner
property from the last example.
It also shows configuring the owner property from markup in index.html
.
<link rel="import" href="https://polygit.org/components/polymer/polymer-element.html">
<dom-module id="configurable-name-tag">
<template>
<!-- bind to the "owner" property -->
This is <b>[[owner]]</b>'s name-tag element.
</template>
<script>
class ConfigurableNameTag extends Polymer.Element {
static get is() { return "configurable-name-tag"; }
// configure owner property
static get properties() {
return {
owner: {
type: String,
value: "Daniel",
}
};
}
}
customElements.define(ConfigurableNameTag.is, ConfigurableNameTag);
</script>
</dom-module>
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://polygit.org/components/webcomponentsjs/webcomponents-loader.js"></script>
<link rel="import" href="configurable-name-tag.html">
</head>
<body>
<!-- configure a property from markup by setting
the corresponding attribute -->
<configurable-name-tag owner="Scott"></configurable-name-tag>
</body>
</html>
Try it out in Plunker:
- Try editing the initial value of
owner
in index.html. Observe how this sets the property directly from your HTML.
Learn more: declared properties
Bind to a property
In addition to text content, you can bind to an element's properties (using
property-name="[[binding]]"
). Polymer properties
can optionally support two-way binding, using curly braces (property-name="{{binding}}"
).
This example uses two-way binding: binding the value of a custom input element (iron-input
)
to the element's owner
property, so it's updated as the user types.
<link rel="import" href="https://polygit.org/components/polymer/polymer-element.html">
<!-- import the iron-input element -->
<link rel="import" href="https://polygit.org/components/iron-input/iron-input.html">
<dom-module id="editable-name-tag">
<template>
<!-- bind to the "owner" property -->
<p>This is <b>[[owner]]</b>'s name-tag element.</p>
<!-- iron-input exposes a two-way bindable input value -->
<iron-input bind-value="{{owner}}">
<input is="iron-input" placeholder="Your name here...">
</iron-input>
</template>
<script>
class EditableNameTag extends Polymer.Element {
static get is() { return "editable-name-tag"; }
// configure the owner property
static get properties() {
return {
owner: {
type: String,
value: 'Daniel'
}
};
}
}
customElements.define(EditableNameTag.is, EditableNameTag);
</script>
</dom-module>
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://polygit.org/components/webcomponentsjs/webcomponents-loader.js"></script>
<link rel="import" href="editable-name-tag.html">
</head>
<body>
<editable-name-tag></editable-name-tag>
</body>
</html>
Try it out in Plunker:
- Edit the placeholder text to see two-way data binding at work.
Note: The <iron-input>
element wraps a native <input>
element and provides two-way
data binding and input validation.
Using <dom-repeat>
for template repeating
The template repeater (dom-repeat
) is a specialized template that binds to an array. It creates one instance of the template's contents for each item in the array.
<!-- import polymer-element -->
<link rel="import" href="https://polygit.org/components/polymer/polymer-element.html">
<!-- import template repeater -->
<link rel="import" href="https://polygit.org/components/polymer/lib/elements/dom-repeat.html">
<dom-module id="employee-list">
<template>
<div> Employee list: </div>
<p></p>
<template is="dom-repeat" items="{{employees}}">
<div>First name: <span>{{item.first}}</span></div>
<div>Last name: <span>{{item.last}}</span></div>
<p></p>
</template>
</template>
<script>
class EmployeeList extends Polymer.Element {
static get is() { return "employee-list"; }
// set this element's employees property
constructor() {
super();
this.employees = [
{first: 'Bob', last: 'Li'},
{first: 'Ayesha', last: 'Johnson'},
{first: 'Fatma', last: 'Kumari'},
{first: 'Tony', last: 'Morelli'}
];
}
}
customElements.define(EmployeeList.is, EmployeeList);
</script>
</dom-module>
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://polygit.org/components/webcomponentsjs/webcomponents-loader.js"></script>
<link rel="import" href="employee-list.html">
</head>
<body>
<employee-list></employee-list>
</body>
</html>
Try it out in Plunker:
- Change the first and last names inside this.employees
- Add another employee by inserting the following text into the array definition after Tony Morelli:
, {first: 'Shawna', last: 'Williams'}
Next steps
Now that you understand these fundamental Polymer concepts, you can build an app with App Toolbox or see a feature overview of the Polymer library.