question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

JS/React interop?

See original GitHub issue

I have previously experimented a bit with Fable and React and really like the experience, but compiling to JS is a significant drawback for a .NET dev like me since 1) it’s a different runtime environment than I’m used to and I have to constantly think about that while coding, and 2) I can’t use any nugets except those few that have been specifically designed to work with Fable (i.e., includes sources in the .nupkg).

I came across Bolero in a recent F# Weekly, and was thrilled, to say the least. I know next to nothing about Blazor or WebAssembly, but AFAIK this project makes it possible to run .NET code directly in the browser, using nugets and a runtime environment that is familiar to .NET devs (please correct me if I’m wrong).

However, having delved into Fable/React/Material-UI a lot recently, I have two questions:

  1. JS interop. The Writing HTML part of the docs shows plain old HTML elements. In order to create the rich/complex UIs often needed today, one might need packages/frameworks like Bootstrap, Bulma, React, or Material-UI (which requires React). The JS ecosystem is incredibly rich. Is there a story here for Bolero? Is it possible? Simple? Are there similar/better alternatives?

  2. Elmish performance. Fable/React/Elmish uses React for rendering, which is lightning quick. Can I expect the Bolero/Elmish combo to scale similarly (using ElmishComponent and ShouldRender similarly to lazy in Elmish.React)?

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:1
  • Comments:13 (1 by maintainers)

github_iconTop GitHub Comments

3reactions
wilsoncgcommented, Sep 2, 2020

I have just gone over the JS interop scenario over the last week or so, specifically I wanted to interop by calling an .NET instance method from JS. After some head scratching, reading the blazor docs and following the approach used in the TryFSharpOnWasm application I have come up with the code below.

@BentTranberg I’d be interested to know if the approach below would work for your bulma date picker scenario?

@Tarmil The bolero docs are fantastic, but they could benefit from showing the interop from JS back to .NET. Would you accept a PR showing something like the code below? I tried searching across github for F# samples which used JSInvokable and found only 3 repositories. If it wasn’t for your TryFSharpOnWasm application I would have really struggled to see all the relevant pieces required.

Hooking into window resize event requires calling into .NET from JS

First we define some Javascript, notice the callback will be provided to the JS environment. We see that the .NET framework will create the machinery for us.

window.generalFunctions = {
    env: {
      hamburgerVisible: false
    },
    getSize: function(){
      var size = { "height": window.innerHeight, "width" : window.innerWidth };
      return size;
    },
    initResizeCallback: function(onResize) {
      window.addEventListener('resize', (ev) => {         
        this.resizeCallbackJS(onResize);
      });
    },
    resizeCallbackJS: function(callback) {
      var size = this.getSize();
      if(size.width < 450 && !this.env.hamburgerVisible)
      {
        this.env.hamburgerVisible = true;
        callback.invokeMethodAsync('Invoke', size.height, size.width);
      }
      if(size.width > 450 && this.env.hamburgerVisible)
      {
        this.env.hamburgerVisible = false;
        callback.invokeMethodAsync('Invoke', size.height, size.width);
      }
    }
  };

This should be loaded after the blazor WASM framework initialization.

<script src="_framework/blazor.webassembly.js"></script>
<script src="/js/windowResize.js"></script>

We then use DotNetObjectReference.Create() to a DotNet JS interop object which is passed into the Javascript defined above. We can define a helper Callback type which will be decorated with JSInvokable, this allows the blazor framework to correctly identify & call the instance method. We create a subscription message during initialization, where the Javascript is instructed to call the Invoke() method on the .NET object. With this mechanism we have achieved JS interop, where a WindowResize message will be dispatched within Bolero on each window.resize DOM event.

type Size(h:int, w:int) =
    member this.Height with get() = h
    member this.Width with get() = w
    new() = Size(0,0)

type Callback =
    static member OfSize(f) =
        DotNetObjectReference.Create(SizeCallback(f))

and SizeCallback(f: Size -> unit) =
    [<JSInvokable>]
    member this.Invoke(arg1, arg2) =
        f (Size(arg1, arg2))

type Message =
    | Initialize
    | WindowResize of Size

let update (jsRuntime:IJSRuntime) message model =
    let setupJSCallback = 
        Cmd.ofSub (fun dispatch -> 
            // given a size, dispatch a message
            let onResize = dispatch << WindowResize
            jsRuntime.InvokeVoidAsync("generalFunctions.initResizeCallback", Callback.OfSize onResize).AsTask() |> ignore
        )
    
    match message with
    | Initialize -> model, setupJSCallback
    | WindowResize size ->
        // handle window resize message
        model, Cmd.none
3reactions
weebscommented, Apr 1, 2019

@cmeeren I came across a Blazor library for Bootstrap / Material you might find interesting:

https://github.com/stsrki/Blazorise

https://blazorise.com/docs/

As far as I can see these are standard Blazor components so you should be able to use them within Bolero

Ex:

comp<TextEdit> [ "Placeholder" => "Some text value..." ] []
Read more comments on GitHub >

github_iconTop Results From Across the Web

Interop layer for consuming React components with other ...
react -interop provides exportComponents and exportCallbacks utilities to make it easy to provide an interop layer over your React components.
Read more >
Interop with React — reagent 1.2.0
reagent: A simple ClojureScript interface to React Documentation for reagent ... expects either a string representing an HTML element or a React Component)....
Read more >
Integrating with Other Libraries
This guide will examine some of the more common use cases, focusing on integration with jQuery and Backbone, but the same ideas can...
Read more >
Component Interop With React And Custom Elements
Today we'll look at how to wrap React components using Custom Elements, allowing us to interop with Web Component-based libraries using the ...
Read more >
reactjs - React component interop
Is there a way to interop between FooBox and Barbox , or do I need to nest both under ReactDOM.render ? And if...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found