本サイトの管理用Webアプリ作成基盤として使用しているSAFE Stackがリニューアルされました。
細かい経緯等はF#でブログ管理用Webアプリ開発ブログ管理用Webアプリ(SAFE Stack)の.NET Core 3.x 移行を参照していただくとして、今回はSAFE Template v2のお話をしたいと思います。

v1 → v2で変わったところ

  • テンプレートがシンプルになった。
  • .NET Core 3.1になった。
  • 各プロジェクトがnetstandard2.0からnetcoreapp3.1に変更。
    • SAFE Documentationはまだnetstandard2.0のままになっている。
  • Sharedが独立したプロジェクトになった。ClientプロジェクトとServerプロジェクトはSharedプロジェクトを参照する形に。
  • Standardはpaketの構成が単一に。前のようにgroup Clientやgroup Serverは無くなった。
  • テストプロジェクトが追加されている。
  • build.fsxにFarmerによるAzureへのデプロイが追加されている。
    • dockerについては"How do I..."に記載されている。

結構、変わってます。
以前の記事、SAFE Stack 開発環境構築でまとめたオプション類は全て無くなり、-m でMinimal版が作成されるのみとなりました。

カスタマイズはドキュメント見てね。という方針のようです。まぁ、これはこれでいいんじゃないでしょうか。
SAFE Stackサンプルのアップグレードも特につまるところ無く出来ました。

Felizについて

今回、久々にSAFE周辺を見ていたら、面白いものを見つけました。
FableのコントリビュータであるZaid-Ajaj氏がFelizというものを作ってます。
フェリスと読みます。ハッピーという意味だそうで。

詳しくは上記GitHubのリンクかドキュメントを見て欲しいのですが、触りだけ。

これまでFableでViewを作成する際は

  div [アトリビュートのリスト] [子要素のリスト]

というような形式で記述していました。これは一見、理にかなっているように思えるのですが、"[" や "]"が沢山出てきて、今見てるのはアトリビュートのリストなのか、子要素のリストなのか非常に分かりにくかったのです。

例えば、こんな感じ。

  form [] [
    Field.div [] [ Label.label [ ] [ str "あなたの手" ] ]
    Field.div [ Field.IsGrouped ]
        [ Control.p [ ]
            [ Button.a
                [ Button.Color IsInfo
                  Button.OnClick (fun _ -> dispatch Guu) ]
                [ str "グー" ] ]
          Control.p [ ]
            [ Button.a
                [ Button.Color IsInfo
                  Button.OnClick (fun _ -> dispatch Choki) ]
                [ str "チョキ" ] ]
          Control.p [ ]
            [ Button.a
                [ Button.Color IsInfo
                  Button.OnClick (fun _ -> dispatch Paa) ]
                [ str "パー" ] ] ]
    Field.div []
      [ Label.label [] [str "結果"]
        Control.p [] [str (sprintf "%s" model.Result)]]
    Field.div []
      [ Label.label [] [str "勝敗"]
        Control.p [] [str (sprintf "%i 勝 %i 敗" model.Win model.Lost)]]
  ]

コンス("[]")がところどころ入っていて、どちらのことなのかわかりにくい!
ちなみにPascalケースなキーワードはBulmaというCSSフレームワークのラッパー(Fulmaといいます)です。

これがFelizスタイルだとこうなります。

  Html.form [
    Bulma.field.div [
      Bulma.label [prop.text "あなたの手"]
    ]
    Bulma.field.div [
      field.isGrouped
      
      prop.children [
        Bulma.control.p [
          Bulma.buttons [
            Bulma.button.button [
              color.isPrimary
              prop.onClick (fun _ -> dispatch Guu)
              prop.text "グー"
            ]
            Bulma.button.button [
              color.isInfo
              prop.onClick (fun _ -> dispatch Choki)
              prop.text "チョキ"
            ]
            Bulma.button.button [
              color.isDanger
              prop.onClick (fun _ -> dispatch Paa)
              prop.text "パー"
            ]
          ]
        ]
      ]
    ]
    Bulma.field.div [
      Bulma.label [prop.text "結果"]
      Bulma.control.p [prop.textf "%s" model.Result]
    ]
    Bulma.field.div [
      Bulma.label [prop.text "勝敗"]
      Bulma.control.p [prop.textf "%i 勝 %i 敗" model.Win model.Lost]
    ]
  ]

ちょっと縦に伸びた感はありますが、リストの前にキーワードが付与される感じになっているので、とてもわかりやすいです。
Bulmaというキーワードから始まっているのはFeliz.Bulma、さきほどのFulmaに対応するものです。
FelizがサポートするHTML要素は最初のHtml.formのようにHtmlというキーワードから始まります。

Felizで記述する際の注意点は、

  • 属性が無い時はリストの中にReactElementを直接並べることが出来る。
  • 属性が有る場合はprop.Childrenの中にReactElementを並べる

といったところでしょうか。ReactElementとはHtml.divなどが返す要素のことです。ちなみに属性はIReactPropertyという型です。
Felizは独自の型を返すわけではなく、Fable.Reactで定義された型を返すので、その気になれば混ぜた書き方も可能です。

また、Reactの関数コンポーネントも、より自然に書けるようになっているので、ElmishはよくわからないがReactは知ってるという方にもオススメです。


まとめ

かなり駆け足で、しかもSAFE Stack v2の話よりもFelizの話の方が長いという、タイトルと合ってない記事になってしまいました。ごめんなさい。

Reactもフックが導入されて、より関数型言語との親和性が高くなりました。
Elmishはシンプルな状態管理が出来ますし、Recoilも既にFeliz.Recoilというものが作られているので、ReactをF#で書く環境は整っていると思います。

SAFE Stackサンプル(GitHub) もSAFE Template v2にアップグレードし、ついでにFelizで少し書き直してみました。よろしかったら参考にしてくださいませ。