Make blazor lazy with custom elements
Tags: blazor, fsharp
Published: 2022-11-05 08:01:54

[Original post is here](https://www.slaveoftime.fun/blog/75a604ec-a59c-4ca7-af67-7d0b0814b361?title=%20Make%20blazor%20lazy%20with%20custom%20elements)

Before I heard blazor supports custom elements which can be used to integrate with blazor with other frameworks like react, angular etc. The way it supports that is by registering blazor component as standard web custom element. When the custom element is loaded it will try to connect to blazor host. For example for blazor server, it will try to reuse or create a websocket connection to asp.net core and initialize that custom element like normal blazor component. For more detail you can check [AspLabs](https://github.com/aspnet/AspLabs/tree/main/src/BlazorCustomElements).

It would be boring if I just introduce that 😎

    Below stuff is using [Fun.Blazor](https://github.com/slaveOftime/Fun.Blazor) as the tech background. Which is just a fsharp wrapper for blazor.

I always use my blog site as a playground to try something. Before I use blazor server to render my site, it works well, but yes not necessary to always build a websocket connection. So I try to render it in static mode and blazor related js is removed, every time you click a link (home page link, and post detail link) it will reload the whole page just like the old times.

There is one feature I want to contact the backend which is when I load post detail page, I want to increase view count. Before I used **htmx**, so it will trigger a POST api **/api/post/{id}/viewcount** when detail loaded. But in this way the backend api is exposed. (Of course I can expose another GET api **/ui/post/{id}/viewcount** to return a html fragment for view count and inject to the current page by **htmx**).

But I will have to create multiple endpoints and use a lot of magic url string to hook those up.

```fsharp
[<Route "/api/post">]
type PostController(postService: PostService) as this =
    inherit ControllerBase()

    [<HttpPost "{id}/viewcount">]
    member _.IncreaseViewCount(id) = task {
        do! postService.IncreaseViewCount id
        return this.Ok()
    }
```

```fsharp
div {
    hxTrigger (HxTrigger(hxEvt.load))
    hxPost $"/api/post/{postId}/viewcount"
    hxSwap_none // I can also swap a <span>View 123</span> if use the /ui/post/{id}/viewcount
}
```

So I was wondering how can I make it easier. I always start with what I want it to be:

```fsharp
CustomElement.create (fun (hook: IComponentHook, postService: PostService) ->
    hook.AddFirstAfterRenderTask(fun _ -> postService.IncreaseViewCount post.Id)
    viewCount post.ViewCount
)
```

I expect it will render static content as below for the first render. 

```html
<fun-node-custom-element node-render-fragment-key="2010792719" />
<script>if (window.initBlazor) { window.initBlazor() }</script>
```

Then the script will try to load blazor scripts and create or re-create connection. And automatically, go to the logic in **CustomElement.create** and return dynamic content.

Things in **CustomElement.create** can run anything which blazor server can run and should run naturally and normally.

All the ideas are implemented in **Fun.Blazor.CustomElements**. So magic is already happened.

## Summary

```html
<body>
    ...
    <custom-element1 /> // connect to blazor server for interaction with backend.
    ...
    <custom-element2 />
</body>
```

We can have multiple **custom-element** and in the same page, they will share the same websocket connection.

I know the use cases will not too much, but for some website which is most for displaying stuff like blog site. Only some part will have complex interaction. And for that part we also want to hide the backend APIs. Then we can use this simple way to do that.

[My blog site](https://github.com/slaveOftime/Slaveoftime.Site) is updated into this way, most parts are static rendered by asp.net core and the view count in post detail page will connect to server again with a websocket connection, and increase the view count, finally return a html fragment to that static page.

It sounds a little bit complex, but when I write it, it is very easy.