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.
So, it should be possible to implement that only for the nice emlish-react stuff.
let mountPoint = document.createElement "div"
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".
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.
If we inherit a class from this. Fable will translate this into "class MyClass extends HTMLElement"
This function will be intiate the ReactDOM.render part. It's a little modified version of the react Program.withReact" function.
This one is also an "Emit".
And now. We see some thing, that looks like our elmish app.
But wait, what's happen with the CSS.
So what we need is to add our style sheets to the ShadowDOM.
At first we need a separate CSS file. Currently it's bundled to the bundle.js.
// Add these 2 line here !!!
// Add these entry to the plugnis array!
Now we have our styles.min.css.
Inside out connectedCallback function we need to add the CSS link.
// Here these 5 lines !!!
Now we had to define some additional boilerplate, so that Fable translate this for us correctly.
Please read the commentaries for further explanations.
// Extend the ShadowRoot with the second method!
// Extend the ShadowRoot class with this member!
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.
After some research, again. It's a problem in React in the ShadowDOM. The React Event have to be retargeted.
So we have to import that library and run retargetEvents(shadowRoot); after we rendered the react stuff.
Now we need some boilerplate, so that our little function will run.
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.
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.
For example the inital start value of the counter.
We can also edit the init function of the counter state itself, so that we give the start value to it. But for the demo purppose this little change is enough.
After we change the init function our code has an error.
let counterStart = getAttribute "counter-start"
This function gives us the value of the attribute.
As always, we nee some boilterplate to let this function work.
In this case I return a string.
We need to parse the string into an int, in order to work as out new init() function parameter.
let counterStart = this.getAttribute "counter-start"
At the end I will also add to scripts to the index.html to polyfill the custom elements and the shadowDOM for browsers like Edge or IE, which aren't currently support web components natively.
So I hope I could helped someone with this. In my case, I maybe will try to develop this idea any further and mayber I can use it for a customer in the future.
And I hope that I will deploy the code on Github.
If you have any critics, questions or other things, please contact me.