# Custom element concepts
To define a custom element, create a class that extends **PolymerElement** and pass the class to the **customElements.define** method.The custom element's name must start with a lower-case ASCII letter and must contain a dash (-).
import {PolymerElement} from '@polymer/polymer/polymer-element.js';
export class MyPolymerElement extends PolymerElement {
...
}
customElements.define('my-polymer-element', MyPolymerElement);
### Polymer element lifecycle
Reaction Description
constructor -Called when the element is upgraded.The constructor is a logical place to setdefault values,and to manually set up event listeners for the element itself.
connectedCallback -Called when the element is added to a document.Can be called multiple times during the lifetime of an element.Uses include adding document-level event listeners.
disconnectedCallback -Called when the element is removed from a document. Can be called multiple times during the lifetime of an element.Uses include removing event listeners added in connectedCallback.
ready -Called during Polymer-specific element initialization. Called once, the first time the element is attached to the document.
attributeChangedCallback -Called when any of the element's attributes are changed, appended, removed, or replaced.Use to handle attribute changes that don't correspond to declared properties.
### Polymer element initialization
ready() {
super.ready();
// do something that requires access to the shadow tree
...
}
The PolymerElement class initializes your element's template and data system during the **ready** callback, so if you override ready, you must call **super.ready()** before accessing the element's shadow tree.
### Defer non-critical work
When possible, defer work until after first paint. The render-status module provides an "afterNextRender" utility for this purpose.
import {PolymerElement} from '@polymer/polymer/polymer-element.js';
import {afterNextRender} from '@polymer/polymer/lib/utils/render-status.js';
class DeferElement extends PolymerElement {
...
constructor() {
super();
// When possible, use afterNextRender to defer non-critical
// work until after first paint.
afterNextRender(this, function() {
this.addEventListener('click', this._handleClick);
});
}
}
### Element upgrades
Adding a definition for an element causes any existing instances of that element to be upgraded to the custom class.
For example:
### Extending other elements
In addition to PolymerElement, a custom element can extend another custom element:
import {MyElment} from './my-element.js';
export class ExtendedElement extends MyElement {
static get is() { return 'extended-element'; }
....
};
customElements.define(ExtendedElement.is, ExtendedElement);
### Sharing code with class expression mixins
A mixin is simply a function that takes a class and returns a subclass:
MyMixin = function(superClass) {
return class extends superClass {
constructor() {
super();
this.addEventListener('keypress', (e) => this._handlePress(e));
}
....
}
}
Or using an ES6 arrow function:
MyMixin = (superClass) => class extends superClass {
...
}
Add a mixin to your element like this:
class MyElement extends MyMixin(PolymerElement) {
static get is() { return 'my-element' }
}
When creating a mixin that you intend to share with other groups or publish, a couple of additional steps are recommended:
- Use the dedupingMixin function to produce a mixin that can only be applied once.
- Define the mixin in an ES module and export it.
For Example,
class MyElement extends MixinB(MixinA(Polymer.Element)) { ... }
At this point, your element contains two copies of MixinB in its prototype chain. dedupingMixin takes a mixin function as an argument, and returns a new, deduplicating mixin function:
mixin-b.js
import {dedupingMixin} from '@polymer/polymer/lib/utils/mixin.js';
// define the mixin
let internalMixinB = (base) =>
class extends base {
...
}
// deduplicate and export it
export const MixinB = dedupingMixin(internalMixinB);
# Declare Properties
Declared properties can specify:
* Property type.Type: constructor
* Default value.Type: boolean, number, string or function.
* Property change observer. Calls a method whenever the property value changes.Type: string
* Read-only status. Prevents accidental changes to the property value.Type: boolean
* Two-way data binding support. Fires an event whenever the property value changes.Type: boolean
* Computed property. Dynamically calculates a value based on other properties.Type: string
* Property reflection to attribute. Updates the corresponding attribute value when the property value changes.Type: boolean
Example
class XCustom extends PolymerElement {
static get properties() {
return {
user: String,
isHappy: Boolean,
count: {
type: Number,
readOnly: true,
notify: true
}
}
}
}
customElements.define('x-custom', XCustom);
### Property name to attribute name mapping
When mapping attribute names to property names:
-Attribute names are converted to lowercase property names. For example, the attribute "firstName" maps to "firstname".
- Attribute names with dashes are converted to camelCase property names by capitalizing the character following each dash,then removing the dashes. For example, the attribute "first-name" maps to "firstName".
### Configuring object and array properties
For object and array properties you can pass an object or array in JSON format:
### Configuring default property values
Default values for properties may be specified in the properties object using the value field, or set imperatively in the element's constructor.
**Default in properties object**
class XCustom extends PolymerElement {
static get properties() {
return {
mode: {
type: String,
value: 'auto'
},
data: {
type: Object,
notify: true,
value: function() { return {}; }
}
}
}
}
**Default in constructor**
constructor() {
super();
this.mode = 'auto';
this.data = {};
}
static get properties() {
return {
mode: String,
data: {
type: Object,
notify: true
}
}
}
### Property change notification events (notify)
When a property is set to notify: true, an event is fired whenever the property value changes. The event name is:
**property-name-changed**
Where property-name is the dash-case version of the property name. For example, a change to **this.firstName** fires **first-name-changed**.
### Read-only properties
When a property only "produces" data and never consumes data, this can be made explicit to avoid accidental changes from the host by setting the readOnly flag to true in the properties property definition. In order for the element to actually change the value of the property, it must use a private generated setter of the convention **_setProperty(value)** where Property is the property name, with the first character converted to uppercase (if alphabetic). For example, the setter for **oneProperty** is **_setOneProperty**, and the setter for**_privateProperty** is **_set_privateProperty**.
class XCustom extends PolymerElement {
static get properties() {
return {
response: {
type: Object,
readOnly: true,
notify: true
}
}
}
responseHandler(response) {
// set read-only property
this._setResponse(response);
}
}
### Reflecting properties to attributes
In specific cases, it may be useful to keep an HTML attribute value in sync with a property value. This may be achieved by setting **reflectToAttribute: true **on a property in the properties configuration object. This causes any observable change to the property to trigger an update to the corresponding attribute.Since attributes only take string values, the property value is serialized to a string.For Example,
class XCustom extends PolymerElement {
static get properties() {
return {
loaded: {
type: Boolean,
reflectToAttribute: true
}
}
}
_onLoad() {
this.loaded = true;
// results in this.setAttribute('loaded', true);
}
}
### Custom deserializers
The type system includes built-in support for Boolean and Number values, Object and Array values expressed as JSON, or Date objects expressed as any Date-parsable string representation. To support other types, you can override the element's **_deserializeValue** method.
_deserializeValue(value, type) {
if (type == MyCustomType) {
return stringToMyCustomType(value);
} else {
return super._deserializeValue(value, type);
}
}
### Attribute serialization
When reflecting a property to an attribute or binding a property to an attribute, the property value is serialized to the attribute.
By default, values are serialized according to value's current type, regardless of the property's type value:
*String. No serialization required.
*Date or Number. Serialized using toString.
*Boolean. Results in a non-valued attribute to be either set (true) or removed (false).
*Array or Object. Serialized using JSON.stringify.
To add custom serialization for other data types, override your element's **_serializeValue** method.
_serializeValue(value) {
if (value instanceof MyCustomType) {
return value.toString();
}
return super._serializeValue(value);
}
### Define a legacy element
Legacy elements can use use the Polymer function to register an element. The function takes as its argument the prototype for the new element. The prototype must have an is property that specifies the HTML tag name for your custom element.For Examples,
// register an element
MyElement = Polymer({
is: 'my-element',
// See below for lifecycle callbacks
created: function() {
this.textContent = 'My element!';
}
});
// create an instance with createElement:
var el1 = document.createElement('my-element');
// ... or with the constructor:
var el2 = new MyElement();
### Legacy lifecycle callbacks
legacy callback Description
created -Called when the element has been created, but before property values are set and local DOM is initialized.Use for one-time set-up before property values are set.Equivalent to the native constructor.
ready -Called after property values are set and local DOM is initialized.Use for one-time configuration of your component after its shadow DOM tree is initialized.
attached -Called after the element is attached to the document.
detached -Called after the element is detached from the document.
attributeChanged -Called when one of the element's attributes is changed.
**Using legacy behaviors with class-style elements**
You can add legacy behaviors to your class-style element using the mixinBehavior function:
import {PolymerElement} from '@polymer/polymer/lib/legacy/class.js';
import {mixinBehaviors} from '@polymer/polymer/polymer-element.js';
class XClass extends Polymer.mixinBehaviors([MyBehavior, MyBehavior2], PolymerElement) {
...
}
customElements.define('x-class', XClass);
# LitElement
## A simple base class for creating custom elements rendered with lit-html.
LitElement uses lit-html to render into the element's Shadow DOM and Polymer's PropertiesMixin
to help manage element properties and attributes. LitElement reacts to changes in properties
and renders declaratively using `lit-html`.
* **React to changes:** LitElement reacts to changes in properties and attributes by
asynchronously rendering, ensuring changes are batched. This reduces overhead
and maintains consistent state.
* **Declarative rendering** LitElement uses `lit-html` to declaratively describe
how an element should render. Then `lit-html` ensures that updates
are fast by creating the static DOM once and smartly updating only the parts of
the DOM that change. Pass a JavaScript string to the `html` tag function,
describing dynamic parts with standard JavaScript template expressions:
* static elements: ``` html`
Hi
` ```
* expression: ``` html`
${disabled ? 'Off' : 'On'}
` ```
* attribute: ``` html`` ```
* event handler: ``` html`` ```
## Getting started
* The easiest way to try out LitElement is to use one of these online tools:
* Runs in all supported browsers: StackBlitz, Glitch
* Runs in browsers with JavaScript Modules: JSFiddle, JSBin, CodePen.
* You can also copy this HTML file into a local file and run it in any browser that supports JavaScript Modules.
* When you're ready to use LitElement in a project, install it via npm. To run the project in the browser, a module-compatible toolchain is required. We recommend installing the Polymer CLI and using its development server as follows.
1. Add LitElement to your project:
```npm i @polymer/lit-element```
2. Create an element by extending LitElement and calling `customElements.define` with your class (see the examples below).
3. Install the Polymer CLI:
```npm i -g polymer-cli@next```
4. Run the development server and open a browser pointing to its URL:
```polymer serve```
> LitElement is published on [npm](https://www.npmjs.com/package/@polymer/lit-element) using JavaScript Modules.
This means it can take advantage of the standard native JavaScript module loader available in all current major browsers.
>
> However, since LitElement uses npm convention to reference dependencies by name, a light transform to rewrite specifiers to URLs is required to get it to run in the browser. The polymer-cli's development server `polymer serve` automatically handles this transform.
Tools like [WebPack](https://webpack.js.org/) and [Rollup](https://rollupjs.org/) can also be used to serve and/or bundle LitElement.
## Minimal Example
1. Create a class that extends `LitElement`.
2. Implement a static `properties` getter that returns the element's properties
(which automatically become observed attributes).
3. Then implement a `_render(props)` method and use the element's
current properties (props) to return a `lit-html` template result to render
into the element. This is the only method that must be implemented by subclasses.
```html
```