Skip to content

Tespa/polymer-decorators

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Travis Build Status

polymer-decorators

A library of decorators to help you write web components with Polymer in TypeScript in a type safe and convenient way, like this:

@customElement('my-element')
class MyElement extends Polymer.Element {

  @property()
  myProperty: string = 'foo';
}

Contents

Installation

  1. Install the decorators with Bower (NPM support coming with Polymer 3.0):

    bower install --save Polymer/polymer-decorators
  2. Import the decorator library in your component definitions:

    <link rel="import" href="/bower_components/polymer-decorators/polymer-decorators.html">
  3. Include type declarations for Polymer (available as of version 2.4) and the Polymer decorators in one your TypeScript source files. You can also add them as sources in your tsconfig.json with include or files.

    /// <reference path="./bower_components/polymer/types/polymer-element.d.ts" />
    /// <reference path="./bower_components/polymer-decorators/polymer-decorators.d.ts" />
  4. Enable the experimentalDecorators TypeScript compiler setting. Use the --experimentalDecorators flag, or update your tsconfig.json to include:

    {
      "compilerOptions": {
        "experimentalDecorators": true
      }
    }
  5. Optionally configure Metadata Reflection to define property types more concisely.

Decorator reference

These decorator factory functions are defined on the Polymer.decorators global namespace object. You can refer to them directly (e.g. @Polymer.decorators.customElement()), or you may prefer to assign them to shorter variables:

const {customElement, property} = Polymer.decorators;

@customElement(tagname?: string)

Define a custom element.

If tagname is provided, it will be used as the custom element name, and will be assigned to the class static is property. If tagname is omitted, the static is property of the class will be used instead. If neither exist, or if both exist but have different values (except in the case that the is property is not an own-property of the class), an exception is thrown.

This decorator automatically calls customElements.define() with your class, so you should not include your own call to that function.

@customElement('my-element')
class MyElement extends Polymer.Element {
  ...
}

@property(options?: PropertyObjects)

Define a Polymer property.

options is a Polymer property options object. All standard options are supported, except for value; use a property initializer instead.

If the Metadata Reflection API is configured, the type option (which determines how Polymer de-serializes Element attributes for this property) will be inferred from the TypeScript type and can be omitted.

@property({type: String, notify: true})
foo: string = 'hello';

@computed(...targets: string[])

Define a computed property.

This decorator must be applied to a getter, and it must not have an associated setter.

Be sure to only read properties that you have declared as dependencies in the computed property definition, otherwise the computed property will not update as expected.

@computed('foo', 'bar')
get fooBar() {
  return this.foo + this.bar;
}

To define a computed property with more complex dependency expressions for which you may want to receive change values as arguments (e.g. sub-properties, splices, wildcards, etc.), or to set additional property options, define a standard property and set its computed option.

@property({computed: 'computeBaz(foo.*)', reflectToAttribute: true})
baz: string;

private computeBaz(fooChangeRecord: object) {
  ...
}

@observe(...targets: string[])

Define a complex property observer.

targets can be a single dependency expression, or an array of them. All observer dependency syntaxes are supported (property names, sub-properties, splices, wildcards, etc.).

@observe('foo', 'bar')
private fooBarChanged(newFoo: string, newBar: string) {
  console.log(`foo is now: ${newFoo}, bar is now: ${newBar}`);
}

@observe('baz.*')
private bazChanged(changeRecord: object) {
  console.log('baz changed deeply');
}

To define a simple property observer, which receives both the old and new values, set the observer option on the property you want to observe to the observer name or (preferably) function reference.

@property({observer: MyElement.prototype.bazChanged})
baz: string;

private bazChanged(oldValue: string, newValue: string) {
  console.log(`baz was: ${oldValue}, and is now: ${newValue}`);
}

@query(selector: string)

Replace this property with a getter that calls querySelector on the shadow root with the given selector. Use this to get a typed handle to a node in your template.

@query('my-widget')
widget: MyWidgetElement;

@queryAll(selector: string)

Replace this property with a getter that calls querySelectorAll on the shadow root with the given selector. Use this to get a typed handle to a set of nodes in your template.

@queryAll('my-widget')
widgets: NodeListOf<MyWidgetElement>

@listen(eventName: string, target: string|EventTarget)

Add an event listener for eventName on target. target can be an object reference, or the string id of an element in the shadow root. The method must match the signature (e: Event) => void, and must have public visibility.

Note that a target referenced by id must be defined statically in the top-level element template (e.g. not in a <dom-if>), because the $ id map is used to find the target upon ready().

To use @listen, your element must apply the DeclarativeEventListeners mixin, which is supplied with this package.

/// <reference path="./bower_components/polymer-decorators/mixins/declarative-event-listeners.d.ts" />

class MyElement extends Polymer.DeclarativeEventListeners(Polymer.Element) {

  @listen('scroll', document)
  onDocumentScroll(event: Event) {
    this.scratchChalkboard();
  }
}

Note that to listen for Polymer gesture events such as tap and track, your element must also apply the GestureEventListeners mixin, which is supplied with Polymer.

class MyElement extends
    Polymer.GestureEventListeners(
    Polymer.DeclarativeEventListeners(
    Polymer.Element)) {

  @listen('tap', 'red-button')
  onTapRedButton(event: Event) {
    this.launchMissile();
  }
}

Metadata Reflection API

To annotate your Polymer property types more concisely, you may configure experimental support for the Metadata Reflection API. Note that this API is not yet a formal ECMAScript proposal, but a polyfill is available, and TypeScript has experimental support.

Also note that the Metadata Reflection polyfill is 47 KB (7 KB gzipped), so be sure to consider strongly whether the cost of shipping this polyfill is worth the convenience for your project.

Without Metadata Reflection, the Polymer property type must be passed explicitly to the decorator factory, because type information is not otherwise available at runtime:

@property({type: String})
myProperty: string;

With Metadata Reflection, the TypeScript type annotation alone is sufficient, because the compiler will emit type information that the decorator can use to automatically set the Polymer property type:

@property()
myProperty: string;

To enable Metadata Reflection:

  1. Enable the emitDecoratorMetadata TypeScript compiler setting. Use the --emitDecoratorMetadata flag, or update your tsconfig.json to include:

    {
      "compilerOptions": {
        "emitDecoratorMetadata": true
      }
    }
  2. Install the Metadata Reflection API runtime polyfill from rbuckton/reflect-metadata:

    bower install --save rbuckton/reflect-metadata
  3. Load the polyfill at the top-level of your application, and in your tests:

    <script src="/bower_components/reflect-metadata/Reflect.js"></script>

FAQ

Do I need this library to use Polymer and TypeScript?

No, you can also use Polymer and TypeScript without additional client libraries. As of Polymer 2.4, TypeScript type declarations are available in the types/ directory. The advantage of using these decorators are additional type safety and convenience. For simple elements and applications, it may be preferable to use the vanilla Polymer API, like this:

/// <reference path="./bower_components/polymer/types/polymer-element.d.ts" />

class MyElement extends Polymer.Element {
  static is = 'my-element';

  static properties = {
    myProperty: {
      type: String
    }
  };

  myProperty: string = 'foo';
}

customElements.define(MyElement.is, MyElement);

What are the performance costs?

The additional JavaScript served for this library is aproximately 8 KB (4 KB gzipped). Benchmarks are not currently available, but we expect minor performance costs. The library generally works by building standard Polymer property definitions at element definition time, so performance costs should be seen at application startup.

Does it work with Polymer 3.0?

Not yet, but support is planned. See issue #10.

Does it work with Polymer 1.0 or 0.5?

No, this library is not compatible with Polymer 1.0 or earlier, because it depends on the ES6 class-based component definition style introduced in Polymer 2.0. Community-maintained TypeScript decorator options for Polymer 1.0 include nippur72/PolymerTS and Cu3PO42/polymer-decorators.

About

TypeScript decorators for Polymer.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • TypeScript 90.9%
  • HTML 9.1%