I am at the process of learning WebSharper, and I am struggling with making some of my logic work on the client-side.
I have a few server-side objects with inheritance hierarchy, which I need to expose to the client-side. They deal with generating different parts of the page as doc fragments -- but on the client.
[<JavaScriptExport>]
type [<AbstractClass>] A() =
abstract member Doc: Map<string, A> -> Doc
...
[<JavaScriptExport>]
type [<AbstractClass>] B() =
inherit A()
[<JavaScriptExport>]
type C() =
inherit B()
The post is updated, see history for the older version.
In my server-side code I have a map associating each object with a given name (as in the strategy design pattern):
let mutable objectMap : Map<string, A> = Map.empty
At some point, the map gets filled with data. This happens once only in the application initialization phase, but is a result of the server-side backend logic.
Now, I need to use those objects on the client-side only, like in the overly-simplified snippet below:
[<JavaScriptExport>]
type C() =
inherit B() // or inherit A()
override this.Doc map =
div [] [ map.["innerDoc"].Doc(map)
On the server-side end I would have:
module ServerSide =
let Main ctx endpoint ... =
// this is the mapping that is being generated on the server, derived somehow from the `objectMap ` above:
let serverMap : Map<string, something> = ...
let document =
[
div [ on.afterRender(fun _ -> ClientCode.fromServerMap(serverMap));][client <@ ClientCode.getDoc("someDoc") @>]
] |> Doc.Concat
Page.Content document
The ClientCode
module would be something that gets compiled to JS and would look like this:
[<JavaScript>]
moduel ClientCode =
let _map : Var<Map<string, A>> = Var.Create <| Map.empty
let fromServerMap (serverMap : something) =
let clientMap : Map<string, A> = // TODO: get the information from server map
_map.Set clientMap
let getDoc (docName : string) =
_map.View.Map(fun m -> m.[docName].Doc(m))
|> Doc.EmbedView
So far I've found out that simply returning the map via an Rpc
during the afterRender
would not work -- either generic JS objects are being returned, or I am receiving a serialization error. Looks like this is the expected behavior for the WebSharper remoting and clinet-server communication.
I know I could just implement my ClientModule.obtainObject
by hardCoding the A
instances inside my map and it does work if I do so, but I need to avoid that part. The module I am developing does not have to know the exact mapping or implementation of the types inheriting from A
(like B
and C
for example), nor what names they have been associated with.
What other approaches I need to use to pass the information from the server-side object map to the client? Maybe use something like Quotation.Expr<A>
in my code?
Update 1: I do not necessarily need to instantiate the objects on the server. Maybe there is a way to send the mapping information to the client and let it do the instantiation somehow?
Update 2: Here is a github repo with a simple representation of what I have got working so far
Update 3: An alternative approach would be to keep on the server a mappping that would use the name of my object type instead of an instance of it (Map<string, string>
). Now if my client code sees ClientAode.C
of whatever the full type name is, is it possible to invoke the default constructor of that type entirely from JavaScript?