Skip to content

Latest commit

 

History

History
166 lines (115 loc) · 6.83 KB

File metadata and controls

166 lines (115 loc) · 6.83 KB
title eleventyNavigation
Decorators
key parent order
Decorators
Components
8

Decorators are special functions that can modify the behavior of classes, class methods, and class fields. Lit uses decorators to provide declarative APIs for things like registering elements, reactive properties, and queries.

Decorators are a stage 2 proposal for addition to the ECMAScript standard, which means they're neither finalized nor implemented in browsers yet. Compilers like Babel and TypeScript provide support for proposed features like decorators by compiling them into standard JavaScript a browser can run.

See the Enabling decorators section for more information.

Lit supplies a set of decorators that reduce the amount of boilerplate code you need to write when defining a component. For example, the @customElement and @property decorators make a basic element definition more compact:

@customElement('my-element')
export class MyElement extends LitElement {
  @property() greeting = "Welcome";
  @property() name = "Sally";
  @property({type: Boolean}) emphatic = true;
  //...
}

{#custom-element}

The @customElement decorator defines a custom element, equivalent to calling:

customElements.define('my-element', MyElement);

The @property decorator declares a reactive property.

See Reactive properties for more information about configuring properties.

Built-in decorators

Decorator Summary More Info
{% api "@customElement" "customElement" %} Defines a custom element Above
{% api "@eventOptions" "eventOptions" %} Adds event listener options. Events
{% api "@property" "property" %} Defines a public property. Properties
{% api "@state" "state" %} Defines a private state property Properties
{% api "@query" "query" %} Defines a property that returns an element in the component template. Shadow DOM
{% api "@queryAll" "queryAll" %} Defines a property that returns a list of elements in the component template. Shadow DOM
{% api "@queryAsync" "queryAsync" %} Defines a property that returns a promise that resolves to an element in the component template. Shadow DOM
{% api "@queryAssignedNodes" "queryAssignedNodes" %} Defines a property that returns the children assigned to a specific slot. Shadow DOM

Importing decorators

You can import all the lit decorators via the lit/decorators.js module:

import {customElement, property, eventOptions, query} from 'lit/decorators.js';

To reduce the amount of code needed to run the component, decorators can be imported individually into component code. All decorators are available at lit/decorators/<decorator-name>. For example,

import {customElement} from 'lit/decorators/custom-element.js';
import {eventOptions} from 'lit/decorators/event-options.js';

Enabling decorators { #enabling-decorators }

To use decorators, you need to build your code with a compiler such as TypeScript or Babel.

In the future when decorators become a native web platform feature, this may no longer be necessary.

Using decorators with TypeScript { #decorators-typescript }

To use decorators with TypeScript, enable the experimentalDecorators compiler option.

"experimentalDecorators": true,

Enabling emitDecoratorMetadata is not required and not recommended.

For TypeScript 3.7 and above a new flag useDefineForClassFields is incompatible with property decorators, and for 4.3 when the target is es2020 or higher it defaults to true.

There are two ways to work around this incompatability: either add a compiler option to force it off:

"experimentalDecorators": true,
"useDefineForClassFields": false,

Alternatively useDefineForClassFields can be used if every decorator annotated property uses declare.

Using decorators with Babel { #decorators-babel }

If you're compiling JavaScript with Babel, you can enable decorators by adding the following plugins:

To enable the plugins, add code like this to your Babel configuration:

plugins = [
  ['@babel/plugin-proposal-decorators', {decoratorsBeforeExport: true}],
  ["@babel/plugin-proposal-class-properties", {"loose": true}],
];

Currently the older legacy mode of Babel decorators is not supported, but this may change as Babel evolves. See the Babel documentation if you want to experiment.

Avoiding issues with class fields

Class fields are a stage 3 proposal for addition to the ECMAScript standard. They currently have a problematic interaction with the decorators proposal in some circumstances.

There are generally no issues when using TypeScript. However, it's important to ensure that the useDefineForClassFields setting in your tsconfig is set to false. This is currently the default setting.

When using Babel, class fields should only be used for properties that are defined with a decorator.

Using the `static properties` syntax along with class fields is not supported.

The following is ok:

@property()
foo = 'bar';

but this is not supported:

static properties = { foo: {} };
foo = 'bar';

Using TypeScript with Babel

When using TypeScript with Babel, it's important to order the TypeScript transform before the decorators transform in your Babel config as follows:

{
  "plugins":[
    ["@babel/plugin-transform-typescript", {"allowDeclareFields": true}],
    ["@babel/plugin-proposal-decorators", {"decoratorsBeforeExport": true}],
    ["@babel/plugin-proposal-class-properties", {"loose": true}],
  ]
}

The allowDeclareFields setting is generally not needed, but it can be useful if you want to define a reactive property without using a decorator. For example,

static properties = { foo: {} };

declare foo: string;

constructor() {
  super();
  this.foo = 'bar';
}