elm が0.19となりしばらくたったので、そろそろバージョンアップするか、といそいそと作業を開始したのですが、
- Dateがなくなった
- innterHtmlが使えなくなった
のが、個人的に地味に効きました。
でも、あまりこれらで苦労したような話はネット上でもあまり見かけないので、なにやら解決方法が他にあるのかもしれません。
Dateがなくなった
elm0.19でDateがなくなり、Time.Posixに統一されました。全世界Time.Posixの値は一緒で、あとはタイムゾーンでずらすだけ、というのがシンプルでいいのかもしれません。
ですが、当初は「やめてくれ~~~~!!」と思いました。扱っているものにはカレンダーや日付を扱うものが多く、それらの大半にDateを使い、更にはDateを便利に扱うdate-extraなどもガンガンに使っておりました、、、、が、、、0.19となった今、それらは現在も影も形もありません
でも、time-extraがあった
しかし、既に0.19に対応したtime-extraが公開されています。
time-extra 1.1.0
時間操作に必要な関数が一通りあります。特にPartsによる時間指定や変換が直感的な操作に非常に役立ちました。
Partsは年、月、日など、これまでのDateタイプと似たような構造をもっているので、
type alias Parts =
{ year : Int
, month : Month
, day : Int
, hour : Int
, minute : Int
, second : Int
, millisecond : Int
}
Partsを指定すれば自分の望む時間のPosix値を得ることができます。(本家core/timeにそのようなcreate系はなかったと思います)
> import Time exposing (..)
> import Time.Extra exposing (..)
> partsToPosix utc (Parts 2018 Sep 26 14 30 0 0)
Posix 1537972200000 : Posix
逆もまた然りで、
> t=partsToPosix utc (Parts 2018 Sep 26 14 30 0 0)
Posix 1537972200000 : Posix
> posixToParts utc t
{ day = 26, hour = 14, millisecond = 0, minute = 30, month = Sep, second = 0, year = 2018 }
: Parts
と、Posix<->Partsで自由に行き来できます。これのおかげで、まずはDateがほしいとは思わなくなりました。
また、このパッケージにはdiffやadd,floor,ceiling,rangeなど一通り時間操作ができます。
これを使って、date-extraパッケージにあった便利な関数たちについてはぼちぼちと自前で移植していきました。ですが、意外とやってみるとすんなりと実装できていったので、逆に勉強になりよかったです。
例えば月の最初の日を求める、というのであれば、
> Time.Extra.floor Month utc t |> posixToParts utc
{ day = 1, hour = 0, millisecond = 0, minute = 0, month = Sep, second = 0, year = 2018 }
: Parts
次の日曜日は
> Time.Extra.ceiling Sunday utc t |> posixToParts utc
{ day = 30, hour = 0, millisecond = 0, minute = 0, month = Sep, second = 0, year = 2018 }
: Parts
などなど、いろいろ組み合わせればdate-extraにあるような便利関数は移行できました。
time zoneもあるけど、utcで割り切り
次にTime.Posixで大きな変化は、タイムゾーンが必須になったことです。・・・が、core/timeバッケージの説明では
Right now this library gives basic Posix and Zone functions, but there are a couple important things it does not cover right now:
- How do I get my time zone?
- How do I get another time zone by name?
- How do I display a time for a specific region? (e.g. DD/MM/YYYY vs MM/DD/YYYY)
とあるように、TaskでTime.hereを通してローカルのタイムゾーンを取得できるだけで、標準でタイムゾーンはutcのみの提供となっており、ユーザーで自由にnycやtokyoなどのタイムゾーンは設定できません。
実環境ではこれでいいのかもしれませんが、テストしたいときに不便で・・・
ですが、これも既にパッケージで
timezone-data 2.0.1
が公開されています。
これを使えば
> import TimeZone exposing (..)
> jpn=asia__tokyo ()
Zone 540 [] : Zone
> posixToParts utc t
{ day = 26, hour = 14, millisecond = 0, minute = 30, month = Sep, second = 0, year = 2018 }
: Parts
> posixToParts jpn t
{ day = 26, hour = 23, millisecond = 0, minute = 30, month = Sep, second = 0, year = 2018 }
: Parts
と自由にタイムゾーンを設定できます。・・・が、日本から出るのでなければ、Partsの日付指定でPosixを作成している限りはUTCオンリーでも問題ないか、と考え、全てUTCで作成してしまいました。
今では逆に・・・
そうこうしているうちに、すっかりDateといった構造を経由するよりも、Time.Posixですっきりと統一された0.19のほうが好きになってしまいました。
innerHtmlが使えなくなった
次に困ったのは、現在蓄えているデータに、一部raw htmlのデータが存在していて、それを表示するのにinnerHtmlを使って直接表示していました。
div [attribute "innerHTML" post.bodyHtml][]
もちろん、XSSにもつながるし、行儀のいいものとは思っていませんが、いかんせんデータ量が多いのと、別にユーザーが入力したものを使うでもなくデータストアしたものを表示するのに使っているので、まぁいいかと使ってきました。が、0.19でついに使えなくなってしまいました。
How to embed raw HTML in Elmより
ここでは、markdownは使えるよ、となっていましたが、自分が試してみてもデコードされてしまい使うことができませんでした。
これは若干ネット上に困ったとの声が
なかにはissueとして上げている人も、、、
property "innerHTML" doesn't work #131
ですが、結局
property "innerHTML" stopped working in 0.19 · Issue #172 · elm/html
ここでevanczその人より提案されていたのが、portsを使ってjavascript側で制御すればいいじゃないの?てことでした。
elm側で
port resetPreviews : () -> Cmd msg
view model=
div [ class "raw", attribute "data-html" htmlstring ] []
と仕込んでおいて、javascript側で
var app = Elm.Main.init();
app.ports.resetPreviews.subscribe(function() {
var kids = document.querySelectorAll('.raw');
for (var i = 0; i < kids.length; i++) {
kids[i].innerHTML = kids[i].getAttribute('data-html');
}
});
(一部改変)で、これならelm界はきれいなままでしょ??みたいな。
ただし、これではDOM更新よりも先にjavascriptコードが実行されてしまい中身が表示されない状態になってしまったので、実際のコードではarowM様の
[Elm] DOM更新後にJSコードを実行する - Qiita
を参考に
var app = Elm.Main.init();
app.ports.resetPreviews.subscribe(function() {
requestAnimationFrame(function () {
var kids = document.querySelectorAll('.raw');
for (var i = 0; i < kids.length; i++) {
kids[i].innerHTML = kids[i].getAttribute('data-html');
}
}
});
としました。(大変助かりました!)
結論
非常にニッチな、「私が困った部分」の内容でしたが、久しぶりに50個くらいのコンパイルエラーが出る中、やはりコンパイラと会話するように開発が進められるelmはいいなぁ、とつくづく実感しました。他の言語よりもそれほどエラー潰しが苦じゃなく感じます。