Polymerを使えば、宣言的記述によりWeb Componentsを簡単に作成することができます。
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>
カスタム要素は、Polymerの提供する以下のような機能を利用でき、シンプルな記述によって複雑でインタラクティブな性質を持ったカスタム要素を容易に構築することができます。:
- カスタム要素の登録
- ライフサイクルコールバック
- プロパティの監視
- Shadow DOMテンプレート
- データバインディング
このチュートリアルでは、何もインストールすることなくPolymerライブラリのクイックツアーを体験できるようにしています。チュートリアル内のPLUNKERのサンプルコードというリンクをクリックすれば、サンドボックスの上でサンプルを試すことができます。
また、ボタンをクリックすれば、各セクションで紹介した機能に関連するドキュメントの該当ページへ移動できます。
カスタム要素を登録
新しいカスタム要素を登録するには、ES6で導入されたclass構文を使いPolymer.Element
クラスを拡張した上で、customElements.define
メソッドを呼び出しカスタム要素をブラウザに_登録_します。この登録によってカスタム要素名とclass名が関連付けられます。なお、カスタム要素の名前はASCII文字で始まりダッシュ(-)を含める必要があります。
<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!
このサンプルでは、ライフサイクルコールバックをして<custom-element>
の初期化時にコンテンツを追加しています。初期化が完了すると、ready
というコールバックが呼び出されます。ready
コールバックは、カスタム要素の生成後にワンタイムの初期化を行いたい場合に利用できます。
Shadow DOMの追加
多くの要素は、独自のUIや動作を実装するために内部にDOMノードを持っています。PolymerのDOMテンプレートを使うことで、カスタム要素にShadow DOMというDOMのサブツリーを作成できます。
<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はカスタム要素の内部にカプセル化されています。
Shadow DOMを使って要素を作成
Shadow DOMを使うことでカスタム要素を柔軟に構築できます。カスタム要素の子は割り当てられるので、Shadow DOMツリーに挿入されたかのようにレンダリングされます。
このサンプルでは、ロゴイメージをCSSでスタイリングされた<div>
タグで囲うことで、シンプルなカスタムタグを作成しています。
<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.
ヒント: <dom-module>
の内部で定義されたCSSのスタイル情報は、カスタム要素のShadow DOM内にスコープされます。そのため、上記サンプルにおいて<picture-frame>
内部のdiv
に対して適用したスタイルルールは、内部の<div>
タグに対してのみ適用されます。
Learn more: Composition & distribution
データバインディングを利用
もちろん予め静的にマークアップしたShadow DOMだけは十分でないと考えるでしょう。多くの場面において、Shadow DOMを動的にアップデートしたいと考えるはずです。そのような場合には、データバインディングというシステムを使いします。
データバインディングを使えば、簡潔なコードでカスタム要素内部で発生した変化をShadow DOMへ反映させることができます。{{}}
という記号を使い、コンポーネント内のプロパティとバインドします。例えば{{}}
は、カスタム要素内のプロパティfoo
の値によって置き換えられます。
<!-- 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.
プロパティの宣言
プロパティは、カスタム要素が外部へ公開するAPIの重要な一部です。Polymerは、宣言的なプロパティをサポートしており、以下のような一般的な機能を提供します。:
- プロパティにデフォルト値を設定
- マークアップからプロパティを設定
- プロパティの変更を監視して事前に指定した処理(observer)を実行
下記サンプルでは、一つ前に紹介したサンプルにowner
プロパティの宣言を加えています。index.html
内のマークアップからowner
プロパティを設定しているのが分かると思います。
<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.
プロパティへのデータバインディング
Shadow DOMのテキストコンテンツだけでなく、カスタム要素のプロパティに対してもバインドすることができます。(property-name="[[binding]]"
という記法を使います。)Polymerのproperties
は、任意のオプションとして双方向バインディングもサポートしています。(property-name="{{binding}}"
のように{{}}
を使います。)
このサンプルでは、双方向バインディング(two-way binding)を使用しています。カスタムinput
要素(iron-input
)のvalue
とowner
プロパティがバインドされており、ユーザーがタイプするとカスタム要素のコンテンツもアップデートされます。
<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.
ヒント: <iron-input>
要素は、ネイティブの<input>
要素のラッパーとして双方向のデータバインディングや入力値のバリデーション機能を提供します。
<dom-repeat>
使いテンプレートを連続して描画
テンプレートリピーター(dom-repeat
)は、配列とのバインドに特化したテンプレートです。配列内の各アイテムにつき一つずつテンプレート内のコンテンツをインスタンス化します。
<!-- 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'}
Learn more: テンプレートリピーター(dom-repeat)
次のステップ
これでPolymerの基本的なコンセプトは理解できたはずです。あとは、build an app with App ToolboxというCLIツールを使って実際にアプリケーションを作成したり、feature overview of the Polymer libraryを参照してPolymerライブラリの機能の概要を理解していってください。