Elm備忘録 ~CustomElementつかっていこうぜ[Popover編]~
問題
- Elmでポップオーバを使おうとおもうとModelにPopoverの状態を保持するためのフィールドがめっちゃ増えんねん。
- 俺は嫌やねん。
- CSSでやるという手段もあるけどまあ置いときましょう。今はBootstrapのやつがいいんです。
type alias Model = {
titlePopoverState: Popover.State
, columnDescribePopoverState1: Popover.State
, columnDescribePopoverState2: Popover.State
, columnDescribePopoverState3: Popover.State
, columnDescribePopoverState4: Popover.State
, columnDescribePopoverState5: Popover.State
}
解
- PopoverをCustomElementにしてElmから呼び出すぞ!
PopoverをCustomElementにラップする
- bootstrapの本家jsを使うとjqueryとお付き合いが発生するので
https://thednp.github.io/bootstrap.native
を使っていくスタイルです。 - CustomElementのお作法にそいながら
connectedCallback
でPopoverを表示するロジックを書いてdisconnectedCallback
で後片付けロジックを書きます。 -
customElements.define('x-popover', PopoverElement);
のx-popoverが要素名になり -
this.getAttribute(アトリビュート名);
で指定しているアトリビュートが要素のアトリビュートとして利用できますdata-placement
がポップオーバーがどこに出るかを指定できるアトリビュートです。top
,left
,right
,bottom
が指定できます。
- いろいろ端折ってますがWebpackでバンドルやで。とか脳内補完してください
const Popover = require("bootstrap.native").Popover;
/**
* TODO クォート統一無法地帯
* https://thednp.github.io/bootstrap.native/
*/
class PopoverElement extends HTMLElement {
attributeChangedCallback(attr, oldValue, newValue) {
}
disconnectedCallback() {
if (this.removeCloseHandler) {
this.removeCloseHandler();
}
}
isSetUped() {
return !!this.instance;
}
setUpBootstrapPopover(popoverparent) {
popoverparent.className = "fas fa-info-circle color-blue ml-5px";
const messageAttr = this.getAttribute('data-message');
const titleAttr = this.getAttribute('data-title');
const placementAttr = this.getAttribute('data-placement');
popoverparent.setAttribute("data-content", messageAttr);
popoverparent.setAttribute("data-title", titleAttr);
this.instance = new Popover(popoverparent, {
template: `<div class="popover" role="tooltip">
<div class="arrow"></div>
<h3 class="popover-header">${titleAttr}</h3>
<div class="popover-body">${messageAttr}</div>
</div>`,
placement: placementAttr
});
this.removeCloseHandler = () => {
// HOGEHOGE
}
}
connectedCallback() {
const popoverparent = document.createElement('span');
if (this.isSetUped()) {
return;
}
this.setUpBootstrapPopover(popoverparent);
this.appendChild(popoverparent);
}
}
customElements.define('x-popover', PopoverElement);
Elmで呼び出しを定義する
-
Html.node "x-popover" [] []
でCustomElementを呼び出すようにしてやる -
Attr.attribute "attr" "value"
でアトリビュートを指定する - PlacementをCustomElementで宣言するのはElmって感じでいいですね。
module Popover exposing (view, Config, Placement(..))
import Html
import Html.Attributes as Attr
type Placement
= Top
| Bottom
| Right
| Left
placementToString : Placement -> String
placementToString pl =
case pl of
Top ->
"top"
Bottom ->
"bottom"
Right ->
"right"
Left ->
"left"
type alias Config =
{ title : String
, message : String
, placement : Placement
}
view : Config -> Html.Html msg
view { title, message, placement } =
Html.node "x-popover" -- ここがミソ
[ Attr.attribute "data-message" message
, Attr.attribute "data-title" title
, Attr.attribute "data-placement" (placementToString placement)
]
[]
呼び出す側
- 下のような感じで呼び出せる
import Popover
view [] [
Html.span []
[
Html.text "むずい用語や"
, Popover.view {
message = "ウンタラカンタラ"
, title = "この用語はな..."
, placement = Popover.Top }
]
]
〆
- CustomElementによってPopoverの状態をCustomElementに閉じ込めてElmから知らん顔できるようになった
- Popoverに限らずElmで状態を管理する価値はないぜ!ってなったものをCustomElementにしてしまえばElmで管理する状態は減る
- 当方DatePickerとかCustomElementにしたりしました。
- 今回でなかったCustomElement -> Elmへのイベント通知とかDatePickerを題材に紹介しちゃいたい
- ElmのModelで状態を持つ価値がないなぁというときにはこういうのもありないんじゃない?