3

I'm trying to put together a JQuery Mobile site using the F# WebSharper Framework. In WebSharper parlance I have created a Pagelet using JQueryMObile controls which is being served by a Sitelet. Everything compiles and runs, the problem is in the html that is generated.

The page I have declared (simplePage) clearly exists in the markup and is marked with the JQueryMobile css class ui-active to make it visible. However, it is surrounded by a div that is also a page but is not marked with the active css class, making it invisible. Therefore my page inside this div is hidden. I am not creating this containing page div. It seems to be a side-affect of loading the JQueryMobile script in the html head. How can I get rid of it?

I have taken the example from http://websharper.com/samples/JQueryMobile. I am using WebSharper version 2.5.125.62 and WebSharper.JQueryMobile version 2.5.4.198. I have one relevant code file shown below followed by the generated html.

Main:

open IntelliFactory.Html
open IntelliFactory.WebSharper
open IntelliFactory.WebSharper.Html
open IntelliFactory.WebSharper.JQuery
open IntelliFactory.WebSharper.Sitelets

type Action = | Home

[<JavaScript>]
module MyJQueryContent =

    let Main() =
        JQuery.Mobile.Mobile.Use()
        let page = Div [
                        Id "simplePage"
                        HTML5.Attr.Data "role" "page"
                        HTML5.Attr.Data "url" "#simplePage"
                        ] -< [
                        Div[Text "content"]
                    ]
        Div [page]
        |>! OnAfterRender (fun _ -> JQuery.Of(page.Body)
                                    |> JQuery.Mobile.JQuery.Page
                                    |> ignore

                                    JQuery.Mobile.Mobile.Instance.ChangePage(JQuery.Of(page.Body)))

[<Sealed>]
type MyJQueryMobileEntryPoint() =
    inherit Web.Control()

    [<JavaScript>]
    override this.Body = MyJQueryContent.Main() :> _

module Pages =

    let HomePage =
        PageContent <| fun context ->
            { Page.Default with
                Title = Some "Index"
                Body = [IntelliFactory.Html.Tags.Div[new MyJQueryMobileEntryPoint()]] }

[<Sealed>]
type Website() =
    interface IWebsite<Action> with
        member this.Sitelet = Sitelet.Content "/" Home Pages.HomePage
        member this.Actions = [Home]

type Global() =
    inherit System.Web.HttpApplication()

    member g.Application_Start(sender: obj, args: System.EventArgs) =
        ()

[<assembly: Website(typeof<Website>)>]
do ()

Output:

html output

Nick
  • 6,366
  • 5
  • 43
  • 62

3 Answers3

3

As a side note, you are creating one of the containing DIVs when you write Div [page] instead of page before adding OnAfterRender, but indeed this is not your problem.

As Omar describes, with jQuery Mobile you need to carefully control when and how the initialization of the page structure takes place, especially when working with dynamic pages. I recall seeing your exact problem before but I can't find that conversation in my email box, however, here is an earlier article that has some useful bits for tapping into JQM page initialization:

http://fpish.net/blog/JankoA/id/3362/201367-jquery-mobile-page-reuse-with-websharper

Adam Granicz
  • 121
  • 2
  • Good tip. `pagebeforechange` fires before showing "main page". However, it fires twice, first time it returns a `string` (URL), second time it returns an `object`. In the article it listens to first event `| :? string as pageUrl ->` – Omar Sep 30 '14 at 12:12
  • Thanks for searching for the email. I've seen the link you've provided but its already too adventurous for what I'm trying to accomplish. I'd like to see a comprehensive example of a JQM site built with WebSharper that includes form submits and server side data retrieval. I've struggled a lot with WS so far - I'm not getting that Pit of Success feeling... – Nick Oct 01 '14 at 10:52
2

When jQuery Mobile framework is loaded, it checks if there is a page div in DOM. If not, it wraps body's content in page div. To control this behavior, you need to prevent jQM's autoInitializePage in order to initialize manually $.mobile.initializePage() whenever you want.

You need to listen to mobileinit event to override autoInitializePage. The code should be placed after jQuery (core) and before jQuery Mobile libraries in head.

/* jQuery.js */
$(document).on("mobileinit", function () {
  $.mobile.autoInitializePage = false;
});
/* jQuery-Mobile.js */

Now you can initialize jQuery Mobile whenever you want by calling $.mobile.initializePage().

Omar
  • 32,302
  • 9
  • 69
  • 112
  • Thanks Omar, that's been useful enough that I can hack a fake page div to prevent JQM wrapping my content. However, I would like some attention from the WebSharper folks before I accept the answer. – Nick Sep 30 '14 at 08:44
  • @Nick I misunderstood your comment, my bad :) it looks like there are better answers than mine. – Omar Sep 30 '14 at 12:07
1

I am a developer of WebSharper. Omar is right, this is a JQM thing, another workaround for it is having a dummy page node in the sitelet. Like this:

module Pages =
    open IntelliFactory.Html

    let HomePage =
        PageContent <| fun context ->
            { Page.Default with
                Title = Some "Index"
                Body = 
                    [
                        Div [HTML5.Data "role" "page"; Id "dummy"]
                        Div [new MyJQueryMobileEntryPoint()]
                    ] }
Jand
  • 391
  • 1
  • 6
  • 1
    Is it doable without _dummy page_ div? Just by initializing jQM manually? – Omar Sep 30 '14 at 12:09
  • That's really what I'm after, better coordination of the JQM events. The hack works, but its only a hack. – Nick Oct 01 '14 at 10:50