Search code examples
f#websharper

Using WebSharper to render SVGs server-side


I'm trying to generate SVGs server-side, passing them in to a client using WebSharper's RPC mechanism. I'm basing this off the websharper-web template, using dotnet core.

For the sake of thoroughness, I render the template like so:

// Site.fs
    let Draw ctx =
        Templating.Main ctx EndPoint.Home "Home" [
            h1 [] [text "Say Hi to the server!"]
            div [] [client <@ Artery.Main() @>]
        ]

I've defined an RPC method that returns my SVG:

module Server =
    [<Rpc>]
    let FetchDrawing () =
        let center = {Svg.Point.X=50; Svg.Point.Y=50}
        let r = 25
        let circle = Svg.Shape.Circle(center=center, radius=r)
        async {
            return circle |> Svg.render
        }
    // Returns an SvgElement.circle with cx, cy, and r set

This is fine so far, but I cannot, for the life of me, figure out how load this properly in the client. The closest I've gotten is:

[<JavaScript>]
module Artery =
    let Main() =
        let circle = Server.FetchDrawing()
        div [] [
            SvgElements.svg [SvgAttributes.height "100"
                             SvgAttributes.width "100"] [
                Doc.Async circle
                ]
            ]

This:

  1. Type checks and compiles
  2. Runs
  3. Throws runtime JavaScript errors

Specifically, I get TypeError: x is not a function. SourceMaps link the error deep into a WebSharper library called Snap.fs, and which sheds... comparatively little light.

Is this a bug in WebSharper? Should this be working? What's the correct way to include an Async<Doc> in a client-side doc like this? If possible, I'd prefer not to use jQuery -- I'd rather return JS that asynchronously loads in the SVG, rather than having jQuery plug it in later -- but I'm open to JQuery answers if that's the best practice here.


Solution

  • The quick answer is no, this will not work as is, unfortunately, and sorry for not thinking more clearly when I gave you guidance earlier on the F# Slack channel. On one hand, it's a WebSharper compiler limitation not to give a warning that you can not pass Doc values from an RPC to the client. The WebSharper documentation page for remoting lists out the requirements on types that can participate on such exchange, and unfortunately Doc doesn't qualify.

    On the other hand, there are proposals to make RPC calls more aligned around serialization, for instance dotnet-websharper/core#930, paving the road to enabling more types to work in RPCs, or one could just wire in support for Doc values in RPCs directly to enable your scenario, simply because they should work. This is one of those few corner cases that need to be covered, indeed.

    What you can do now to circumvent the problem is changing your RPC to return a custom SVG representation (some AST of shapes, for instance) that fits your needs, and use that to convert to a Doc on the client side.