Friday, January 21, 2011

WebSharper sitelets: building a two-page website

Let me show the simplest possible self-contained example involving WebSharper sitelets that are coming with the 2.1 release. You define a website with two pages (and two actions):

Let us quickly mash up a template for the website using F#-embedded HTML combinators. A template is just a function taking the body and decorating it:

Two things to notice:

  1. F# lets you define your own syntax, and the example makes liberal use of that (=>).
  2. Instead of generating URLs by hand, you ask the context to create a link to the action. This ensures safety: renaming Home action makes the project stop compiling, and moving it to a different URL keeps the links correct.

Now, you define the sitelets:

HomePage and AboutUsPage are single-page sitelets, with a single URL and a single Action. They are combined into a website by the Sum operator.

Now, a little bit of administrative boilerplate:

And you are done! Let’s browse:

s2 s1

So far so good. The pages have the expected URLs and the menu links work.

The above could have been accomplished by any reasonable web framework. Let us push the limit and spice it up a bit now. Let us add a few lines of F# that will actually compile to JavaScript:

So there is a button that changes its title when clicked. AND there is a control. Now, this while the Button lives on the client-side completely (constructs DOM nodes, in fact), the Control executes a quantum leap: the constructor executes on the server, and the body on the client. But that means you can use the Control to glue the server and the client together. Let us update the AboutUs page:

That’s it. The user will now see a clickable, JavaScript-enabled, F#-implemented button, right where you expect it. No script tags to worry about, no dependency chasing, no “ondocumentready” worries, it just works:

s2

Below is the complete listing. As soon as the 2.1 release becomes public, you will able to enjoy running it yourself. Stay tuned!

7 comments:

  1. Nice post, Im looking forward to trying out Websharper.

    ReplyDelete
  2. Hello Anton. Firstly i want to say thank you for a really fresh in mind web platform I hope it will be very popular.
    Can you please clarify one think? Can I call a “pure” server code (without Rpc call from client) anywhere except control “constructor”?
    This code works:
    type RefreshAllCarsControl() =
    inherit IntelliFactory.WebSharper.Web.Control ()
    do CarRequestForm.RefreshAll()
    []
    override this.Body =
    P["All request refreshed" |> Text] :> IPagelet
    This not:
    type RefreshAllCarsControl() =
    inherit IntelliFactory.WebSharper.Web.Control ()
    do CarRequestForm.RefreshAll()
    []
    override this.Body =
    P["All request refreshed" |> Text] :> IPagelet
    override this.Render(x) =
    //this code never be called
    base.Render(x)

    And I am very confused by this, because I expect to see call of Render(HtmlTextWriter) because my control is subclass of IntelliFactory.WebSharper.Web.Control which is subclass of Web.UI.Control.

    ReplyDelete
  3. It's [JavaScript] attribute instead of [], sorry.

    ReplyDelete
  4. Спасибо, Алексей, это похоже на баг. Я добавил:

    https://bugs.intellifactory.com/websharper/show_bug.cgi?id=311

    Похоже что-то странное происходит с наследованием.

    ReplyDelete
  5. @Алексей - наследование из System.Web.UI.Control позволяет использовать этот же самый класс в ASPX страницах, однако использование сайтлетов совершенно обходит ASPX механизм и не вызывает Render. Вместо этого используется интерфейс IntelliFactory.Html.Html.INode.

    Я рад буду узнать какую задачу вы пытаетесь решить - тогда мы сможем упростить этот сценарий в следующем ВебШарпере.

    See discussion at: https://bugs.intellifactory.com/websharper/show_bug.cgi?id=311

    ReplyDelete
  6. Has something changed since 2.1 was released? Just tried this, but nothing is created in the html folder. The scripts were generated, however.

    ReplyDelete
  7. @Ryan, are you trying this with HTML-generating sitelets? You should hint to the generator which actions are there (are to be printed to HTML):

    member this.Actions = [HomePage; ShowBlogs]

    ReplyDelete