Building WebComponents with Fable - Elmish - React

I see tons of good examples in Fable and build nice web apps with Fable and Elmish and React. But I have a client, which needs to build WebComponents. So I've done some research, it should be possible to build web components with the nice way of Fable Elmish. And it is possible.

But there are some troubles to get t to work. So I write here the Steps to success. Maybe I or someone else can write an extension to Fable encapsulate all the boiler plate stuff.

Okay, define a class which inherits from an HTMLElement.
The method connectCallback() is one of the life cycle methods of a webcomponent.
And what we do is run our Program stuff inside this method.

Easy isn't it? Okay, we need some boilerplate stuff. Like "createShadowRoot" or "initReact".

3. The Boilerplate Stuff to translate this into the right javascript

1. HTMLElement()
Fable has already an interface of HTMLElement. But we don't need this. We need an empty abstract class. We need to flag this as global to avoid that this class will be translated.

```fsharp
[<Global>]
type HTMLElement() = class end
```
If we inherit a class from this. Fable will translate this into "class MyClass extends HTMLElement"

2. createShadowRoot()
Here we use Emit to generate the matching javascript code to create a ShadowDOM root. There is also a createShadowRoot() function in Javascript, but the Polyfill for Browsers, that doesn't support custom elements only recognize this function. (Hint: the type ShadowRoot is defined on No. 4)fsharp [<Emit("this.attachShadow({ mode: 'open' });")>] let createShadowRoot() : ShadowRoot = jsNative

3. document.creatElement is part of Fable

4. shadowRoot.appendChild
Here is the missing ShadowRoot type. Here we defint one member to append a HTMLElement (The Fable interface, not our placeholder in No. 1)fsharp [<Global>] type ShadowRoot() = [<Emit("$0.appendChild($1);")>] member this.appendChild (el:Fable.Import.Browser.HTMLElement) = jsNative

5. |> initReact mountPoint
This function will be intiate the ReactDOM.render part. It's a little modified version of the react Program.withReact" function.
```fsharp
// Common for the "lazyView2With" function
open Elmish.React.Common

The font isn't getting right, if you do not add the styles to the index.html. This is something for further research.

So well ... It's looking nice.

If we go to the Counter and press plus or minus, than nothing will happen. Also if we enter our name on the home screen. But hey, we can navigate.

So what do we do now?

7. The problems with the events inside the ShadowDOM

After some research, again. It's a problem in React in the ShadowDOM. The React Event have to be retargeted.
Lucky for us, Lukas Bombach published a package to fix this error: (Lukas. Thank you: https://github.com/LukasBombach)

After restart dotnet fable yarn-start, it works like a charm. Pressing the buttons, our webcomponent works as it should.

If you add a new <web-component></web-component> tag in the index.html, you will see, that the web components works independently. With one exception. The navigation is synchronized, because the browser address will be used to navigate.

This (maybe) issue is for another day.

8. The attributes of a web component

As you know, you can also add attributes to a web component.
So the idea is, that we modify the init functions to accept parameter. So we can change the initial state of our component with attributes.
So add this "counter-start" attribute to the web-component tag.
And maybe add a second below that without an attribute. like this: