Edited at

[Elm] Attributes.batch で Attributes をもっと柔軟に使いこなそう

ElmでViewの共通化をしたいとき、別にViewを頑張ってつくらなくてもAttributeを"Mixin"として使い回せばいいことがわりとあります。

たとえば

module Mixin exposing (..)

import Html exposing (Attribute)
import Html.Attributes as Attributes

bigButton : Attribute msg
bigButton =
Attributes.class "button button_big"

のような関数を定義して使いまわすイメージです。

この例は簡単なので別にこれでもいいんですが、場合によっては複数の Attribute をセットにして使いまわしたいことがしばしばあります。

module Mixin exposing (..)

import Html exposing (Attribute)
import Html.Attributes as Attributes

submitButton : Bool -> List (Attribute msg)
submitButton sending =
[ Attributes.class "button"
, Attributes.class "button_submit"
, Attributes.disabled sending
]

このようにリストにするだけなのですが、これをViewで呼ぼうとするとどうなるでしょうか?

module Main exposing (..)

import Html exposing (Html)
import Html.Attributes as Attributes
import Mixin

submitButton : Model -> Html msg
submitButton model =
Html.button
(Mixin.submitButton model.sending)
[]

これを見る限りは特に問題なさそうですね!

じゃあ、Mixin.bigButtonMixin.submitButton を同時に使いたい時はどうなるでしょうか?

bigSubmitButton : Model -> Html msg

bigSubmitButton model =
Html.button
( Mixin.bigButton :: Mixin.submitButton model.sending)
[]

ちょっと嫌な予感がしてきましたね?

じゃあ、ピンク色の大きな送信ボタンを作ろうと思ったらどうなるでしょうか?

ピンク色のボタンにするための Mixin が以下の型をしているとします。

pinkButton : List (Attribute msg)

こうなりますね?

bigPinkSubmitButton : Model -> Html msg

bigPinkSubmitButton model =
Html.button
( Mixin.bigButton :: Mixin.submitButton model.sending ++ Mixin.pinkButton)
[]

わーお。

それじゃあ今度は、model.brokenTrue のときのみ class="broken" を付けてボタンがぶっ壊れるようにしてみましょう!

bigPinkHotSubmitButton : Model -> Html msg

bigPinkHotSubmitButton model =
Html.button
(Mixin.bigButton
:: Mixin.submitButton model.sending
++ Mixin.pinkButton
++ (if model.broken then
[ Attributes.class "broken"
]

else
[]
)
)
[]

うぅううううううああああああああ!!!!!

もういやだぁあああああああああ!!!!!!!!!!

おうちにかえるぅうううううううう!!!!!!!!!!!

sakuraaaaa.jpeg


追記 2019/8/11

最近は elm-mixin というライブラリを作ったので、そちらをメインに使っています。


Attributes.batch で解決する

たとえばこれを以下のように書けるとしたらいかがでしょうか?

module Mixin exposing (..)

...
...

submitButton : Bool -> Attribute msg
submitButton sending =
Attributes.batch
[ Attributes.class "button"
, Attributes.class "button_submit"
, Attributes.disabled sending
]

pinkButton : Attribute msg
pinkButton =
Attributes.batch
[ ...
, ...
]

module Main exposing (..)

...
...

bigPinkHotSubmitButton : Model -> Html msg
bigPinkHotSubmitButton model =
Html.button
[ Mixin.bigButton
, Mixin.submitButton model.sending
, Mixin.pinkButton
, if model.broken then
Attributes.class "broken"

else
Attributes.none
]
[]

めちゃくちゃ可読性が向上してませんか?

これを実現する上でポイントとなっているのが次の2つの関数です。

Html.Attributes.batch : List (Attribute msg) -> Attribute msg

Html.Attributes.none : Attribute msg

ちょうど Cmd とか Sub とかの Cmd.batch Cmd.none と似たイメージですね!


arowM/html

さて、便利さはわかりましたが、残念ながらこれらの関数は elm/html にも elm-community/html-extra にも含まれていません。

これらを使うには arowM/html乗り換える 必要があります。

詳細は省きますが、elm/html加えて 他のライブラリを読み込む形では Html.Attributes.batch のようなものは実現できません。

そこで、arowM/htmlelm/html置き換える という戦略をとっています。

もちろん互換性は保ってありますので、今まで書いてきた elm/html に依存したコードは arowM/html に乗り換えてもそのまま同じように動きます。

それに加えて、上記の batchnone が使えるようになるのです。


arowM/html への置き換え方

elm/html にさよならして arowM/html に置き換えるのはとても簡単です。



  1. elm.json の dependencies にある elm/htmlarowM/html に変える

  2. elm-stuff ディレクトリを削除する

  3. コンパイルしなおす

  4. コンパイラが「こいつらを elm.json の "indirect" に追加してね」と教えてくれるので指示にしたがう

これだけでコンパイルが通るようになり、batchnone の力が手に入ります!

さくらちゃんにご飯をあげる

さくらちゃんをもっと見る

他の記事を見る

sakura.jpeg