デコレータパターンプラス
最近、興味深い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のinput
はname='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 <= できない
でも、悲しまないでください。この問題は簡単にデコレータパターンで解決できます。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
でそのinput
をslot
にかえなくてはいけません。
<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
を作る機能を設定しましょう。
LitElement
とlit-html
普通にLitElement
を使う時はlit-html
のsyntaxだけについて考えなくてはいけません。LitElement
のrender()
の中に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を使えます。
デコレータパターンプラスでLitElement
のrender()
の中に<slot/>
を置きます。そしてlight DOMからの<input/>
が見えます。render()
の前にconnectedCallback()
で<input/>
があるかどうか確かめます。<input/>
がなかったらlight DOMに新しいのを作ります。作るのはlit-html
を使うから簡単に更新できます。sugoi-input
は毎回のupdated()
で、作った<input/>
があったら、それに現在のpropertiesをあたえます。それで普通のLitElement
のtemplateとデコレータパターンプラスの<input/>
のtemplateを管理します。
その他の利点
form
をシリアル化する時に使えるのはとても便利ですが他の利点もあります。親form
がsugoi-input
にアクセスできたらinput
がオートコンプリートできます。簡単なアプリを作りたかったらauto-completeは重要です。ユーザーはオートコンプリートする事でもっと速くform
が書けます。
別の理由は、デコレータパターンプラスを使ったら、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のこみ先生にこれを書くのを助けてくれたことに感謝します。