Login

RSS

ATOM

slaveOftime
#feliz
#fsharp
#blazor
#computation_expression

2021/07/18 18:27:26

Experiment on computation expression style for Blazor with F#

Before I use Feliz style for the auto generated dsl but find its a little redundant, so let`s try CE style

Experiment on computation expression style for Blazor with F#

Before I use Feliz style for the auto generated DSL but find it’s a little redundant, so let`s try CE style. The problem with Feliz style is obvious, every time you want to add attribute or some property you need to type the whole name for that component like below:


mudLayout.create [
    mudLayout.rightToLeft false
    mudLayout.childContent [
        mudAppBar.create [
            mudAppBar.color Color.Primary
            mudAppBar.elevation 25
            mudAppBar.dense true
            mudAppBar.childContent [
                mudIconButton.create [
                    mudIconButton.icon Icons.Material.Filled.Menu
                    mudIconButton.color Color.Inherit
                    mudIconButton.edge Edge.Start
                    mudIconButton.onClick (fun _ -> openMenu.Publish not)
                ]
            ...


For default DOM elements like div, span etc. they belong to a very short named type like html, attr. So you can write it very clearly like:


html.div [
    attr.styles [
        style.marginTop 20
        style.marginBottom 20
    ]
    html.text "hello world"
    ...

 

Anyway I wanted my other generated bingdings can be more clear and I started to experiment on CE style, it should look like this:

 

mudLayout() {
    rightToLeft false
    childContent [
        mudAppBar() {
            color Color.Primary
            elevation 25
            dense true
            childContent [
                mudIconButton() {
                    icon Icons.Material.Filled.Menu
                    color Color.Inherit
                    edge Edge.Start
                    onClick (fun _ -> openMenu.Publish not)
                }
             ...


I think it looks fatastic and very clear, but after I implemented it I found some drawbacks:

  • The intelliense is not very good. In Visual Studio after you typing some char, it brings a lot of options to you and even you press alt+m, you still get some invalid options.

/api/image/47f49595-94f6-4aad-a760-28e2e904eb71

  • Fsharp can not always infer the types, sometimes you need to call CAST manually:
html.watch (openMenu, fun isOpen ->
    mudDrawer() {
        open' isOpen
        elevation 25
        variant DrawerVariant.Persistent
        childContent [
            mudDrawerHeader() {
                linkToIndex true
                childContent [
                    mudText() {
                        color Color.Primary
                        typo Typo.h5
                        childContentStr "Have fun ✌"
                    }
                ]
            }
            navmenu
        ]
        CAST
    }
)

html.watch has multiple overrides, one is aking for return a IFunBlazorNode, even mudDrawer implemented interface IFunBlazorNode, the compiler still give error only if you cast it maunally.

childContent only asks for `IFunBlazorNode list`, F# can infer that so you do not need add cast.


  • For CE (computation expression) style, F# do not support override CustomOperation(if the base class has same name for CustomOperation within the child class, fsharp compiler will raise error), so I have to flat inherited properties which may make the binding files a little bigger. And it does not feel good. And the conflicts made me also have to change some property name, like in default attr there is a min method, but if I inherited it then I have to change min to min' or something else to make the conflict go away.


Overall, I kind of like this CE style. But it is very opinionated, so in the Fun.Blazor.Cli I added support for both style for now.


For more information please check Fun.Blazor repo