comment
We are happy to announce the availability of WebSharper 3.4.14, which you can download here. Here are the main highlights of this release.
ASP.NET-hosted OWIN
Previously, a bug prevented the WebSharper.Owin middleware from running on top of ASP.NET using Microsoft.Owin.Host.SystemWeb. This has now been solved, and the following is now possible:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
open global.Owin open Microsoft.Owin open WebSharper open WebSharper.Owin module Site = let Main = Application.SinglePage(fun ctx -> Content.Text "Hello world!") type Startup() = member this.Configuration(app: IAppBuilder) = app.UseSitelet(System.Web.HttpRuntime.AppDomainAppPath, Site.Main) |> ignore [<assembly:OwinStartup(typeof<Startup>)>] do ()
For this you need to install the following NuGet packages in your web application: WebSharper
, WebSharper.Owin
, Microsoft.Owin.Host.SystemWeb
and Mono.Cecil
.
On the topic of Owin, the Suave team recently pre-released support for running Owin middleware; we will soon publish a blog entry about running WebSharper applications within Suave.
WebSharper UI.Next
A number of new features have also been added to UI.Next.
Data binding: abstract references and lenses
Until now, UI.Next interactive elements such as Doc.Input
were only able to interact directly with a Var
of primitive type. This was quite limiting in terms of how you could structure your data at the root of the dataflow graph. For example, if you wanted to have a ListModel
with a record type for items, and display it with input fields to edit the individual record fields, then those record fields would need to have type Var<'T>
themselves.
The new abstract reference type IRef<'T>
relieves this restriction. IRef<'T>
is an interface type that represents data that can be directly read or written, or observed by a View<'T>
. Conceptually, that's exactly what Var<'T>
is, and of course Var<'T>
implements this interface; but not only. There are now methods on Var<'T>
and ListModel<'K, 'T>
that return IRef<'U>
s where 'U
represents a part of the original 'T
. This concept is called lensing. For example:
1 2 3 4 5 6 7 8
type Person = { FirstName: string; LastName: string; Id: Key } let me : Var<Person> = Var.Create { FirstName = "Loïc"; LastName = "Denuzière"; Id = Key.Fresh() } let myFirstName : IRef<string> = me.Lens (fun p -> p.FirstName) (fun p n -> { p with FirstName = n }) let myFirstNameInput = Doc.Input [] myFirstName
In the above code, myFirstName
is an IRef<string>
that lenses into the FirstName
field of me
. Retrieving myFirstName
's value retrieves the FirstName
field of the current value of me
, and setting myFirstName
performs a record update on the current value of me
. It is therefore possible to create an input field that is bound to the FirstName
field of me
. Note how the type Person
is able to be entirely immutable.
Even more useful is lensing into ListModel
s. See for example the following editable list of people:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
let people = ListModel.Create (fun p -> p.Id) [] let firstNameOf : Key -> IRef<string> = people.LensInto (fun p -> p.FirstName) (fun p n -> { p with FirstName = n }) let lastNameOf : Key -> IRef<string> = people.LensInto (fun p -> p.LastName) (fun p n -> { p with LastName = n }) let displayPeople = ul [ people.View |> Doc.ConvertSeqBy people.Key (fun pid person -> li [ Doc.Input [] (firstNameOf pid) Doc.Input [] (lastNameOf pid) ] ) ]
Creating a lensed reference is currently somewhat awkward, since you have to provide both the get and update functions. We are planning on providing a macro that will be able to infer the update function based on the getter function, making lenses more convenient to use.
Submitters
A simple way to allow the dataflow to react to isolated events is to have a view whose value is taken from an input view, but only gets updated when events occur. This was already possible using View.SnapshotOn
, but somewhat more involved than it needed to be. The new type Submitter<'T>
simplifies this task:
1 2 3 4 5 6 7 8 9 10
let rvInput = Var.Create "the input data" let submit = Submitter.Create rvInput.View "no data submitted yet" div [ Doc.Input [] rvInput Doc.Button "Submit" [] submit.Trigger p [ text "You entered: " textView submit.View ] ]
Other UI.Next features
Add some instance equivalents to static members: the functions
View.Map
,View.Bind
andListModel.View
are now available as instance members on the corresponding types, allowing slightly shorter code for those who prefer this style.ListModel.Key: both as a static and instance member, retrieves the function that is used by a
ListModel
to get the key of an item. Typically needed byView.Convert*By
orDoc.Convert*By
(see the lens example above).Doc.BindView: it is rare, when inserting a dynamic doc using
Doc.EmbedView
, to have aView<Doc>
handy; instead, you generally have aView<'T>
that needs to be mapped to aView<Doc>
. The new functionDoc.BindView : ('T -> Doc) -> View<'T> -> Doc
combines these two steps in a single function call.Event handlers that can use a
View
's current value: it is fairly common to need the current value of a reactiveView
inside the callback of an event handler. It is now very easy to do so using the functionAttr.HandlerView
, or methods such ason.clickView
.Templating: allow an element to be both a hole and a subtemplate. This is useful for example when the hole is to be filled with a list of items, and the subtemplate describes how these items will be displayed.
doc.On*
methods for standard events have been added, akin to theon.*
attributes. They also exist in server-side friendly version, taking a quotation as argument.on.*
attributes have been converted to camelCase; for example,on.animationend
is nowon.animationEnd
.
Bug fixes
#464: Fixed the client-side JSON encoding of
option<_>
union case arguments.#463: Track dependencies from the body of
[<Macro>]
-annotated functions.Fixed the type of
JQuery.Position
fields fromint
tofloat
.UI.Next#28: correctly map from string the value of
Doc.IntInput
andDoc.FloatInput
.
Future plans
As mentioned in previous blog entries, our intention is to merge UI.Next into the main WebSharper distribution and to obsolete Html.Client
and Html.Server
. We are planning to have this done for version 3.5.
At the same time, we are starting to implement UI.Next-based Formlets and Piglets. Piglets are pretty much usable already, while Formlets are still in a more early phase. In both cases, we are seeing huge gains compared with the IntelliFactory.Reactive-based counterparts in terms of code simplicity and safety. Look forward to being able to use Piglets and Formlets within UI.Next!
Happy coding!
Note, the "ASP.NET-hosted OWIN" application augments the "Asp.Net --> Katana (Owin)" application template.