このcodelabでは、LitElementを使ったカスタムエレメントの作り方について学べます。ボタンをトグルする簡単なLitElementエレメントを作ります。完成イメージは以下のような感じになります。
これは以下のような簡単なマークアップで利用可能になります。
<icon-toggle></icon-toggle>
このcodelabでは、LitElementを使って作業するための重要なコンセプトについても紹介していきます。
もし全てのコンセプトについて理解できなくても心配しないでください。それらの内容はLitElementの公式ドキュメントにより詳しく書かれています。
チュートリアルを始める前に、以下のソフトウェアがあることを確認してください:
以下の項目について、基本的なスキルや知識が必要になります:
Gitはバージョン管理ツールです
git --version
インストールが成功していれば、Gitのバージョンが表示されるはずです。
もしGitのバージョン番号がgit version 2.19.1
のように表示されない場合は、次のリンクを参照してみることをおススメします。公式ドキュメント - Gitのインストール.
NodeはJavaScriptの実行環境です。npmはNodeのパッケージ管理ツールです。これらはNodeをインストールすると両方ともインストールされます。
npm install npm@latest -g
現時点で node -v
は v10.14.2
でした
現時点で npm -v
は 6.4.1
でした
バージョン番号が表示されなかったら 公式ドキュメントのインストールガイドを参照してください
次のコマンドでリポジトリをクローンしてください。
git clone https://github.com/Polymer-Japan/litelement-first-element.git
ディレトリをクローン先に移動してから、npmコマンドで依存するパッケージをインストールします。
cd litelement-first-elements npm install
インターネット回線が遅いと、とても時間がかかる場合があります....
インストールが終了すると、以下のようなディレクトリ構造になります。
作業する主なファイルはicon-toggle.js
です。このファイルにはカスタムエレメントの定義が入っています。
デモの実行方法はとても簡単です。まだアプリは何も実装していませんが、以下のコマンドを実行してみましょう。
npm start
すると、Polymerの開発サーバーが動いて、デモがブラウザ(chrome)の新しいタブで開きます。アイコントグルは表示されず、テキストだけが表示されるはずです。どうってことないかもしれませんが、これで全てがうまく動いていることが確認できます。
次に、画面にアイコンを表示するための簡単なエレメントを作ってみましょう。
このステップでは、以下のようなことを学習できます。
エディタでicon-toggle.js
ファイルを開いてください。このファイルにはカスタムエレメントのスケルトンが入っています。
既存コードを見ながら進めましょう。
import { LitElement, html } from 'lit-element';
import '@polymer/iron-icons/iron-icons.js';
import '@polymer/iron-icon/iron-icon.js';
キーポイント:
import
はES6 Module importです。アプリケーションで利用するエレメントやモジュールをインポートします。iron-icons
と iron-icon
をインポートします。これらは、この後で登場します。次はエレメント自体を定義していきます。
class IconToggle extends LitElement {
render() {
return html`
<style>
/* local DOM styles go here */
:host {
display: inline-block;
}
</style>
<!-- local DOM goes here -->
<span>Not much here yet.</span>
`;
}
}
キーポイント:
LitElement
を継承したクラスを作成します。render
関数を定義します。render
はlit-htmlのTemplateResult
のインスタンスを返却する必要があります。html
ヘルパーを使うとJavaScriptのテンプレートリテラルからTemplateResult
インスタンスを生成できます。(html
ヘルパーは lit-element.js
モジュールからインポートできます)render
内の<style>
エレメントを使うと、Shadow DOMにscopedな(カプセル化された)スタイルを定義できます。これはドキュメントの他の部分には影響を及ぼしません。:host
擬似クラスは、定義するカスタムエレメントそのもの(この場合は<icon-toggle>
エレメント自体)です。
window.customElements.define('icon-toggle', IconToggle);
キーポイント:
customElements.define
メソッドの第二引数にエレメントクラスを指定します。エレメントの基本構造にふれたところで、Shadow DOMテンプレートを編集してみましょう。
local DOM goes here
というコメントの後に書いてある<span>
タグを見つけてください。
<!-- local DOM goes here -->
<span>Not much here yet.</span>
`;
<span>
タグを、以下のように<iron-icon>
に置き換えてみてください。
<!-- local DOM goes here -->
<iron-icon icon="polymer">
</iron-icon>
`;
キーポイント:
<iron-icon>
エレメントはアイコンを表示するカスタムエレメントです。今は "polymer" という名前をハードコーディングしています。Shadow DOMで利用できる新しいCSSセレクターがいくつかあります。icon-toggle.js
ファイルの:host
セレクターはすでに紹介しました。これは<icon-toggle>
エレメントそのもののスタイルを指定します。
<iron-icon>
エレメントのスタイルを指定するために、<style>
タグ内のCSSを以下の内容に書き換えてください:
<style>
/* local styles go here */
:host {
display: inline-block;
}
iron-icon {
fill: rgba(0,0,0,0);
stroke: currentcolor;
}
:host([pressed]) iron-icon {
fill: currentcolor;
}
</style>
キーポイント:
<iron-icon>
タグはSVGアイコンを使っています。fill
やstroke
は、SVG固有のCSSプロパティです。アイコンの塗りつぶしと輪郭の色をそれぞれ設定します。:host()
関数は、引数で指定されたセレクタが一致するホスト要素(エレメント)に適用されます。このときの[pressed]
は標準のCSS属性セレクタで、icon-toggle
エレメントにpressed
属性が設定されている場合に、スタイルが適用されます。これまでの修正で、カスタムエレメント定義は以下のようになっていると思います。
import { LitElement, html } from 'lit-element';
import '@polymer/iron-icons/iron-icons.js';
import '@polymer/iron-icon/iron-icon.js';
/**
* `icon-toggle`
* Get started creating custom elements with LitElement
*
* @customElement
* @demo demo/index.html
*/
class IconToggle extends LitElement {
render() {
return html`
<style>
/* local styles go here */
:host {
display: inline-block;
}
iron-icon {
fill: rgba(0,0,0,0);
stroke: currentcolor;
}
:host([pressed]) iron-icon {
fill: currentcolor;
}
</style>
<!-- local DOM goes here -->
<iron-icon icon="polymer">
</iron-icon>
`;
}
}
window.customElements.define('icon-toggle', IconToggle);
ホスト要素の値に応じてメッセージを表示するようにするため、constructor
を追加してisFav
属性の初期値を設定するように編集してください。
class IconToggleDemo extends LitElement {
constructor() {
super();
this.isFav = false;
}
デモページをリロードしてください。ハードコーディングしたアイコン表示のトグルボタンが表示されるはずです。
1つのトグルだけが押されたようなスタイルになっています。そのタグにはpressed
属性が設定されているためです。しかし、トグルボタンをクリックしても、まだトグルは動きません。pressed
プロパティを変更するコードがまだないためです。
今のところエレメントは、変化しません。このステップでは、アイコンをマークアップ上から指定するための属性の使い方と、JavaScriptからプロパティを使う方法について、基本的なAPIを紹介します。
まず、データバインディングから始めましょう。 <iron-icon>
エレメントを探して、icon
属性の値を"polymer"
から"${this.icon}
"に変更してみましょう。
<!-- local DOM goes here -->
<iron-icon icon="${this.icon}">
</iron-icon>
キーポイント:
icon
は、後でトグルボタンエレメントのプロパティとして定義します。デフォルト値はまだありません。icon="${this.icon}"
と記述することでデータバインディングを利用できます。トグルボタンエレメントのicon
プロパティの値を<iron-icon>
のicon
プロパティにリンクします。以下の例のように、エレメントのマークアップで記述するか、JavaScriptを使用してicon
プロパティの値を設定できます(このコードをプロジェクトに追加する必要はありません)。
<icon-toggle icon="favorite"></icon-toggle>
var myToggle = document.querySelector('icon-toggle');
myToggle.icon = "favorite";
続いて、icon
プロパティの宣言を追加します。
次のようなstatic get properties
関数をIconToggleクラスに追加してください:
class IconToggle extends LitElement {
static get properties() {
return {
icon: String,
};
}
キーポイント:
static get properties
関数をエレメントクラスに追加します。この関数は、プロパティ宣言を含むオブジェクトを返す必要があります。String
)だけを指定します。properties
オブジェクトは、さらにいくつかの機能をサポートしています。pressed
プロパティを利用できるようにするため、以下のように変更します。
constructor() {
super();
this.pressed = false;
}
static get properties() {
return {
icon: String,
pressed: {
type: Boolean,
reflect: true
}
};
}
キーポイント:
constructor
メソッドでデフォルト値を指定します。reflect
をtrue
に設定すると、プロパティ値が変更されたとき属性の値が追従します。これにより、icon-toggle[pressed]
のような属性セレクタを使用して要素にスタイルを適用できます。この修正でエレメントの pressed
と icon
プロパティが動くようになりました。
デモページをリロードすると、前のステップまでハードコーディングされていたアイコンが、星とハートのアイコンに変わって表示されているはずです。
星とハートがどこで指定されたのか興味があれば、demo/icon-toggle-demo.js
を見てください。以下のように記述されています。
<icon-toggle icon="star"></icon-toggle>
<icon-toggle icon="star" pressed></icon-toggle>
もちろん、クリックできないボタンはボタンではありません。ボタンをトグルするには、イベントリスナーを追加します。iron-icon
エレメントにイベントリスナーを追加するには、以下のように@click
プロパティを要素に追加します。
<iron-icon icon="${this.icon}" @click="${this.toggle}">
キーポイント:
@イベント名
を利用すると、イベントを取得できるようになります。イベントリスナーが呼び出すハンドラーを追加します。
toggle() {
this.pressed = !this.pressed;
}
attributeChangedCallback(name, oldval, newval) {
super.attributeChangedCallback(name, oldval, newval);
if(name === 'pressed') this.dispatchEvent(new CustomEvent('pressed-changed', { detail: this.pressed }));
}
キーポイント:
attributeChangedCallback
を利用すると、属性値の変更を監視できるようになります。値が変更されたらpressed-changed
イベントを投げてicon-toggle
エレメント利用者に状態が変わったことを通知します。icon-toggle.js
ファイルを保存し、デモをリロードします。ボタンを押すと、押した状態と押していない状態を切り替えることができるはずです。
これまで、ベーシックな機能のボタンを作りました。しかし、押された状態と押されていない状態の両方ともに、標準テキストカラーを使用しています。ちょっと派手にしたいと思ったら、どうしたら良いでしょう?
Shadow DOMは、ユーザーが意図せずにエレメントの内部にスタイルを適用するのを防ぎます(同時に外部からスタイルを指定できなくなります)。こうしたときのために、カスタムプロパティを使うと、エレメント内のスタイルをユーザーが設定できるように特定のプロパティセットを提供できます。
var
関数を使って、エレメント内部にカスタムプロパティを適用します。
background-color: var(--my-custom-property, defaultValue);
--my-custom-propertyはカスタムプロパティ名で、常に2つのダッシュ(--
)で始まり、defaultValueはカスタムプロパティが設定されていない場合に使用される(オプションの)CSS値です。
エレメントの<style>
タグを編集し、現在のfill
とstroke
の値をカスタムプロパティに変更します。
<style>
/* local styles go here */
:host {
display: inline-block;
}
iron-icon {
fill: var(--icon-toggle-color, rgba(0,0,0,0));
stroke: var(--icon-toggle-outline-color, currentcolor);
}
:host([pressed]) iron-icon {
fill: var(--icon-toggle-pressed-color, currentcolor);
}
</style>
SVGのデフォルト値として、color
を設定するだけで<icon-toggle>
のスタイルを変更することもできますが、他のオプションを使ってみます。 demo/icon-toggle-demo.js
を開き、以下のようにカスタムプロパティを設定します
<style>
:host {
font-family: sans-serif;
--icon-toggle-color: lightgrey;
--icon-toggle-outline-color: black;
--icon-toggle-pressed-color: red;
};
</style>
デモページをリロードするとカラフルになっているはずです。
これでチュートリアルは終わりです。基本的なUIやAPIとカスタムスタイリングプロパティを持つ要素を作成しました。
もし作業に問題があったときは、完成版のコードを参照してください。