デコレータパターンプラス

デコレータパターン + LitElementlit-html = 😍

westbrook
11 min readDec 4, 2018

Read this post in English

“silhouette of man standing inside structure” by Rene Böhmer on Unsplash

最近、興味深いshadow DOMの使い方をしています。それはバカか?便利か?分かりませんがそれが好きだから皆にそれについて教えたいです。今私はcustom elementで作ったform elementsを使っているんですが多分他の物を作る時に使っても良いです。先ずどのような問題を解決しますか?

この例のような要素はLitElementとcustom elementsとshadow DOMを一緒に使ったら簡単に作れます。

<sugoi-input></sugoi-input>...customElements.define('sugoi-input', class extends LitElement {
render() {
return html`<input />`;
}
});

あなたがそうやって簡単にsugoi-inputの中で設計や機能や子DOMの秘密を守れると思ったら間違いないです。本当にその便利なやり方ができます。ほとんどの人がcustom elementsとshadow DOMを使うのはその方法ができるからです。しかし、そのelementをformの中に使ったらすぐに面倒臭い事がわかります。要素のshadow DOMの中のinputは普通のシリアルかするのでは見えません。そのためsugoi-inputのshadow DOMのinputname='sugoi’があったら次のコードを使えません。

<form>
<sugoi-input></sugoi-input>
<input name="futsuu" value="普通" />
</form>
...customElements.define('sugoi-input', class extends LitElement {
render() {
return html`<input name="sugoi" />`;
}
});
...let form = document.querySelector('form');
let formData = new FormData(form);
console.log(formData.get('futsuu')); // 普通 <= できる
console.log(formData.get('sugoi')); // null <= できない
“silver fork and knife on plate” by Thought Catalog on Unsplash

でも、悲しまないでください。この問題は簡単にデコレータパターンで解決できます。sugoi-inputで普通のinputをデコレートしたらもう一回そのformをシリアル化できます。

<form>
<sugoi-input>
<input name="sugoi" value="凄い" />
</sugoi-input>
<input name="futsuu" value="普通" />
</form>
...let form = document.querySelector('form');
let formData = new FormData(form);
console.log(formData.get('futsuu')); // 普通 <= できる
console.log(formData.get('sugoi')); // 凄い <= できる、やっら!

どうやったらできる?

shadow DOMを使いながら<slot/>でlight DOMをshadow DOMに投影します。英語ですけどこの記事は便利な<slot/>の使い方について教えてくれます。ただLitElementでそのinputslotにかえなくてはいけません。

<sugoi-input>
<input name="sugoi" value="凄い" />
</sugoi-input>
...customElements.define('sugoi-input', class extends LitElement {
render() {
return html`<slot></slot>`;
}
});

この変更の後カスタムスタイルを使いたかったら普通はinput {}のようなCSSを::slotted(input) {}に変えなくてはいけません。

この方法は本当にformを作る仕事を大変にします。いつも使う時に<sugoi-input></sugoi-input>の中に<input/>を置かなくてはいけません。最初のレベルのhtmlで覚えておくのはもっと簡単です。それも最初のレベルのhtmlでデコレータパターンを使ったらjavascriptを使わない人もサイトを使う事ができます。しかしながら<sugoi-input></sugoi-input>を使いたいところでは、必ずそのlight DOMの<input/>を管理しなくてはいけません。それは覚えておくべき事です。覚えなくて良いようにsugoi-inputの中にinputを作る機能を設定しましょう。

LitElementlit-html

普通にLitElementを使う時はlit-htmlのsyntaxだけについて考えなくてはいけません。LitElementrender()の中にlit-htmlのsyntaxを書いたらLitElementはそれ以外の仕事をします。LitElementはとても便利ですがimport {render} from ‘lit-html/lib/shady-render.js’も使ったらデコレータパターンプラスができるようになります。さて、デコレータパターンプラスとは何ですか?

custom elementだけ使ったらinputはshadow DOMから来ます。普通のデコレータパターンだけ使ったらinputはlight DOMから来ます。デコレータパターンプラスを使ったらinputはlight DOMから来ますがそこからinputがなかったらcustom elementがそこに置かれます。では、どうやってそれができますか?

<form>
<sugoi-input>
<input name="sugoi" value="凄い" />
</sugoi-input>
<sugoi-input
name="sugoi"
value="凄い"
></sugoi-input>
</form>
...import {render} from ‘lit-html/lib/shady-render.js’customElements.define('sugoi-input', class extends LitElement {
static get properties() {
return {
name: { type: String },
value: { type: String }
};
}
connectedCallback() {
this.buildLightDOMInput();
}
render() {
return html`<slot></slot>`;
}
updated() {
this.updateLightDOMInput();
}
buildLightDOMInput() {
if (this.$element || this.querySelector('input')) return;
this.$element = this.initGeneratedInput;
}
updateLightDOMInput() {
if (this.$element) this.renderInput();
}
initGeneratedInput() {
this.renderInput();
return this.querySelector('input');
}
renderInput() {
render(this.inputTemplate(), this, this.localName);
}
inputTemplate() {
return html`
<input
name="${this.name}"
.value="${this.value || ''}"
/>
`;
}
});

この例で親formはシルアル化する時に両方のsugoi-inputの書き方にアクセスできます。最初のレベルのhtmlでもアプリコードの中でも簡単にこのsugoi-inputを使えます。

デコレータパターンプラスでLitElementrender()の中に<slot/>を置きます。そしてlight DOMからの<input/>が見えます。render()の前にconnectedCallback()<input/>があるかどうか確かめます。<input/>がなかったらlight DOMに新しいのを作ります。作るのはlit-htmlを使うから簡単に更新できます。sugoi-inputは毎回のupdated()で、作った<input/>があったら、それに現在のpropertiesをあたえます。それで普通のLitElementのtemplateとデコレータパターンプラスの<input/>のtemplateを管理します。

その他の利点

formをシリアル化する時に使えるのはとても便利ですが他の利点もあります。親formsugoi-inputにアクセスできたらinputがオートコンプリートできます。簡単なアプリを作りたかったらauto-completeは重要です。ユーザーはオートコンプリートする事でもっと速くformが書けます。

ユーザーのbrowserのauto-completeを使うと良いです。

別の理由は、デコレータパターンプラスを使ったら、password managersはinputを認識できます。LastPassやkeeperやSticky Passwordがユーザーに選択肢を与える事ができます。これでユーザーがサイトをもっと使いやすくなります。

これから

デコレータパターンプラスは良いと思いますが、この記事の中の使い方はまだ新し考え方です。これより便利な使い方があったら一緒に調べましょう。色々な実験は興味深いです。inputの他のattributesについて考えると良いでしょう。placeholderやautocompleteやpatternもとても便利ですがこの記事の使い方はそれについて考えていません。nameとvalueのinputの中への置き方をすると良いかもしれません?

<sugoi-input
name="sugoi"
value="凄い"
placeholder="何ですか?"
autocomplete="name"
pattern="[a-z]{4,8}"
><sugoi-input>
<sugoi-input>
<input
name="sugoi"
value="凄い"
placeholder="何ですか?"
autocomplete="name"
pattern="[a-z]{4,8}"
/>
<sugoi-input>

labelの書き方についても考えなくてはいけません。light DOM、shadow DOM、custom element、どこから来ると良いですか?<slot/>を使ったら全部のhtmlを一つの中に置くと良いですか?色々な名前がある<slot/>を使えば良いですか?accessibilityはとても重要だから答えを見つけなくてはいけません。

<sugoi-input
name="sugoi"
value="凄い"
label="今日は?”
><sugoi-input>
<sugoi-input>
<label for="sugoi">今日は?</label>
<input
id="sugoi"
name="sugoi"
value="凄い"
/>
<sugoi-input>

普通のinputの機能の他に何の機能を足すといいですか?error messagingやsearch aheadやprefixとsuffixは興味深いです。JavaScriptがなかったらどんな機能があると良いですか?

それは私からの質問です。あなたも質問があったら下にコメントをどうぞ。

私のLingo Live講師麻美さんとHills Learningのこみ先生にこれを書くのを助けてくれたことに感謝します。

--

--