Actually, the Google Maps extension isn't particularly tied to the Html.Client API; the type of mapElement.Body in this code is JavaScript.Dom.Element, ie. the standard DOM element type. It is possible to use it with UI.Next thanks to Doc.Static and a bit of DOM API:

1
2
3
4
5
6
7
8
let Sample buildMap : Doc =
    let e = JS.Document.CreateElement("div")
    e.SetAttribute("class", "mapContainer")
    let center = new LatLng(37.4419, -122.1419)
    let options = new MapOptions(center, 8)
    let map = new Google.Maps.Map(e, options)
    buildMap map
    Doc.Static e

With the caveat that the Map is now created before the element is inserted in the DOM and Google Maps requires the element to have a fixed height at the time the Map object is created. That is why I immediately give the element a class, with a corresponding <style>.mapContainer{height:400px}</style> in my HTML.

To have reactive bits such as the latitude and longitude, you will need wire them by hand:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let rvLat = Var.Create(37.4419)
let rvLng = Var.Create(-122.1419)

let Sample buildMap : Doc =
    let e = JS.Document.CreateElement("div")
    e.SetAttribute("class", "mapContainer")
    let center = new LatLng(rvLat.Value, rvLng.Value)
    let options = new MapOptions(center, 8)
    let map = new Google.Maps.Map(e, options)
    buildMap map
    map.AddListener("center_changed", fun _ ->
        let center = map.GetCenter()
        rvLat.Value <- center.Lat()
        rvLng.Value <- center.Lng()
    ) |> ignore
    Doc.Concat [
        Doc.Static e
        View.Map2
            (fun lat lng ->
                let center = map.GetCenter()
                if center.Lat() <> lat && center.Lng() <> lng then
                    map.SetCenter(LatLng(lat, lng))
                Doc.Empty)
            rvLat.View rvLng.View
        |> Doc.EmbedView
    ]

Note the View.Map (fun v -> some_imperative_code v; Doc.Empty) rv |> Doc.EmbedView trick to run an imperative callback on a View without the potential memory leak issues of View.Sink. We'll probably add a nicer helper for that in an upcoming version of UI.Next.

As far as other extensions go, most of them aren't tied to Html.Client either, only a few are; Formlets and JQueryUI come to mind. It is easy to put a UI.Next.Doc inside an Html.Client.Element with Doc.AsPagelet, but the other way around is tricky. You can simply do Doc.Static e.Body, but then any OnAfterRender callback will not be run, and as you guessed, the semantics to have a UI.Next equivalent to OnAfterRender wouldn't be trivial.

By on 5/27/2015 6:21 AM ()

I thank you for your explanations and the code snippets above.

I started a brand new UI.Next SPA after making sure I had the latest WebSharper (3.1.6.156) for Visual Studio, and I wrapped your code in some amended boilerplate (the original sample code runs in a webpage just fine):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
namespace UINextGM

open WebSharper
open WebSharper.JavaScript
open WebSharper.JQuery
open WebSharper.UI.Next
open WebSharper.UI.Next.Html
open WebSharper.UI.Next.Notation

[<JavaScript>]
module GoogleMaps =
    open WebSharper.Google.Maps

    let rvLat = Var.Create(37.4419)
    let rvLng = Var.Create(-122.1419)

    let Sample buildMap : Doc =
        let e = JS.Document.CreateElement("div")
        e.SetAttribute("class", "mapContainer")
        let center = new LatLng(rvLat.Value, rvLng.Value)
        let options = new MapOptions(center, 8)
        let map = new Google.Maps.Map(e, options)
        buildMap map
        map.AddListener("center_changed", fun _ ->
            let center = map.GetCenter()
            rvLat.Value <- center.Lat()
            rvLng.Value <- center.Lng()
        ) |> ignore
        Doc.Concat [
          Doc.Static e
          View.Map2
              (fun lat lng ->
                let center = map.GetCenter()
                if center.Lat() <> lat && center.Lng() <> lng then
                    map.SetCenter(LatLng(lat, lng))
                Doc.Empty)
            rvLat.View rvLng.View
          |> Doc.EmbedView
        ]
    
[<JavaScript>]
module Client =
    open GoogleMaps    
    
    let Main =
     
      Sample (fun _ -> ())
      |> Doc.RunById "main"

My first problem was that it would not compile with the latest NuGet version of WebSharper.Google.Maps. Error message:

Error 2 Failed to deserialize metadata for: WebSharper.Google.Maps, Version=3.1.0.0, Culture=neutral, PublicKeyToken=...

I got around this by linking to one I had built myself in a separate project a couple of weeks ago. Maybe I've broken my Visual Studio environment somehow - but I did start with a brand new solution.

My second problem is that I don't see a map when I run the now compiling code above. I've tried the simpler version of Sample too. I also stripped the content out of the main DIV in index.html.

I have probably have done something stupid, but I would welcome any suggestions.

By on 5/27/2015 8:19 AM ()

Do you have CSS somewhere that gives .mapContainer a fixed height?

For the metadata error: you might have an outdated websharper and/or ui.next; try updating them, then closing and reopening VS.

By on 5/27/2015 8:54 AM ()

Thanks again Loïc, I had missed the CSS requirement in your answer in my haste to get it working. And it does the trick. Please find a way to replace CSS with a WebSharper DSL!

I'll mark this question as answered, but, I am still experiencing DLL hell with WebSharper.Google.Maps when I try to start from scratch with the latest WebSharper VS IDE download (which includes UI.Next), which I will follow up on when I have more energy (I'd rather be working on the app itself).

By on 5/27/2015 2:39 PM ()

(removed accidental duplicate message)

By on 5/27/2015 8:19 AM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper