By Loïc Denuzière on Wednesday, February 25, 2015 — 2 comments

WebSharper 3.0-alpha for Xamarin Studio / MonoDevelop is now available! Core team

We have updated the WebSharper Xamarin Studio / MonoDevelop add-in for the latest WebSharper 3.0.36-alpha. It is now easier than ever to get started with WebSharper 3.0 on Mac or Linux!

Here is how to install it:

  • Open the add-in manager.

  • Make sure that you have the F# Language Binding installed.

  • In the Gallery, add a new repository with the following address:

    https://raw.githubusercontent.com/intellifactory/monodevelop.websharper/master/repository/

  • The WebSharper add-in is now available under the "Web Development" category. Select it and click "Install".

Voilà! The "New Project" and "New Solution" dialogs now have a new category for WebSharper. In this category you can find the same templates that are provided by the Visual Studio extension and CloudSharper, to quickly start new projects using WebSharper.

You can check the sources for the addin here.

Happy coding!

By András Jankó on Wednesday, February 25, 2015 — 2 comments

WebSharper-3.0.36-alpha released Core team

This release brings the last batch of breaking changes we have planned before 3.0 stable. The three bigger items we have left are updating/fixing TypeScript output, reviewing code mapping and adding more documentation.

Namespace changes

We have shortened namespace names by changing every occurence of IntelliFactory.WebSharper to WebSharper. This affects base package and all extensions. Also IntelliFactory.JavaScript is renamed to WebSharper.Core.JavaScript. DLLs are similarly renamed with the exception of IntelliFactory.WebSharper.dll which is now WebSharper.Main.dll.

WebSharper.Formlets in a separate package

The Formlets library is now contained in the NuGet package WebSharper.Formlets. Its dependency, IntelliFactory.Reactive is in another new package IntelliFactory.Reactive.

JSON Sitelets

You can now create REST APIs easily by using F# types. See previous announcement.

User Session in Sitelets and Rpc functions

Up until now, the module WebSharper.Sitelets.UserSession could be used to manage user login. Unfortunately, functions in this module have two major drawbacks:

  • They use ASP.NET-specific functionality, and therefore cannot be used with other hosts such as self-hosted OWIN.
  • They assume that they run on the same thread that is responding to the request. This assumption is unreasonable, especially when using functions such as Content.PageContentAsync.

For these reasons, the following changes are made:

  • The module WebSharper.Sitelets.UserSession is marked as obsolete.
  • The type Sitelets.Context<'T> now contains a field UserSession, which can be safely used in asynchronous sitelets. Note that unlike the old UserSession, its methods are asynchronous.
  • The new function WebSharper.Web.Remoting.GetContext() can be used in Rpc functions to retrieve a thread-safe user session manager. It must be called from the Rpc function's thread, but the resulting object can be used asynchronously. For example:
1
2
3
4
5
6
7
8
9
[<Rpc>]
let DoSomethingIfLoggedIn() =
    let ctx = Web.Remoting.GetContext()
    async {
        let! loggedIn = ctx.UserSession.GetLoggedInUser()
        match loggedIn with
        | None -> return "Error: not logged in"
        | Some username -> return! DoSomething username
    }
  • This functionality is implemented both in the built-in ASP.NET module and in WebSharper.Owin.

JavaScript interop

  • Previous .ToEcma() methods for converting a .NET type to its EcmaScript binding equivalent has been obsoleted. Renamed to .JS property.
  • Previous .ToDotNet() methods are now a .Self property on WebShaper.JavaScript. types.
  • JavaScript.Function is now a base class for all JavaScript.FuncWith... types (giving strong typing to JavaScript functions).
  • Methods on JavaScript.Function has been renamed to ApplyUnsafe, CallUnsafe, BindUnsafe to avoid overloading with the strongly-typed methods on descendant classes.

WebSharper Interface Generator

  • The generic helper for more than 4 type parameter is now GenericN n - fun typeParamList -> ...
  • Generic % can be used to add the same type parameters to a list of members.
  • Generic * can be used to add the same type parameters to a to a ClassMembers value (created by Instance [...] or Static [...]).
  • These generic helpers all add type parameters in addition to previously defined ones on the members returned by the inner function, instead of overwriting the generics list.
  • Type parameters are automatically renamed on library generation to avoid name clashes.

Other improvements

  • A compile warning is given when a tupled or curried lambda or variable with the same types are coerced to obj. This can help avoiding some mistakes, for example writing

    1
    
    New [ "compare" => fun (a, b) -> a - b ] 

    and passing it to JavaScript code expecting a function that takes two arguments. However, this is translated to a JavaScript function taking a single 2-element array argument. The former is the much more common scenario, so on this warning, change it to

    1
    
    New [ "compare" => FuncWithArgs(fun (a, b) -> a - b) ] 

    to pass the right function. Otherwise, you can use the Function.As static method to wrap the lambda and and get rid of the warning.

  • [<OptionalField>] attribute now can be used on class or record types. This makes all option valued fields of the type represented as present/missing JavaScript properties in the translation.

Fixes

  • #340: Generic methods of a generic type used wrong type parameters for generating the member. This could have affected some of our extensions.
By András Jankó on Wednesday, February 18, 2015 — 0 comments

CloudSharper 0.9.28 released Core team

This release fixes some issues introduced in last release, and improves tooltips and autocomplete.

Fixes

  • #588: Pressing Tab/Shift-Tab with a single cursor works now as in Visual Studio.
  • #589: Pressing . for autocomplete fixed, also works for (.
  • #587: Autocomplete works for .fsx files.
  • #591: Save in File menu and Ctrl+S fixed.

Happy coding!

By Loïc Denuzière on Friday, February 13, 2015 — 0 comments

Upcoming in WebSharper 3.0: serving REST APIs, easy as pie! Core team

WebSharper Sitelets are a wonderful way to build websites quickly and safely. They can automatically manage your URLs in a type-safe way, and generate HTML markup using simple F# combinators. In WebSharper 3.0, we are extending Sitelets with the capability to create REST APIs with unrivaled simplicity.

Requests

Just like you can currently type these lines in F#:

1
2
3
type Action =
	| [<CompiledName "listing">] Listing of pagenum: int
    | [<CompiledName "article">] Article of id: int * slug: string

to tell WebSharper that your site will be served on these URLs:

1
2
/listing/1
/article/135/upcoming-in-websharper-30

You can now also specify all the information necessary to serve a web API on a given HTTP method and with the given JSON body, simply by using a couple attributes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type Action =
	| [<Method "GET"; CompiledName "article">]
    	GetArticle of id: int
    | [<Method "POST"; CompiledName "article"; Json "data">]
    	PostArticle of data: ArticleData

and ArticleData =
	{
    	author: string
        title: string
        tags: Set<string>
        summary: option<string>
        body: string
    }

The following requests are now accepted:

1
GET /article/135

1
2
3
4
5
6
7
8
9
POST /article

{
  "author": "loic.denuziere",
  "title": "Upcoming in WebSharper 3.0: serving REST APIs, easy as pie!",
  "tags": ["websharper", "fsharp"],
  "summary": "WebSharper 3.0 is coming with an (...)",
  "body": "WebSharper Sitelets are a wonderful way to (...)"
}

The JSON serialization provides all the niceties possible to be friendly with F# types:

  • F# records are represented as JSON objects;
  • list<'T>, 'T[] and Set<'T> are representad as JSON arrays;
  • Map<string, 'T> is represented as a flat JSON object;
  • F# fields of type option<'T> are represented as a JSON field that is present if Some or absent if None;
  • F# unions are represented as JSON objects using the union field names, and a separate named field to indicate the union case (the name of this field is specified in an attribute).

Responses

Of course, a REST API is not just parsing requests, but also writing responses. For this too, WebSharper 3.0 has you covered. A new function Content.JsonContent allows you to serve any F# value as JSON with zero hassle:

1
2
3
4
5
6
7
let mySite = Sitelet.Infer <| function
	| GetArticle id ->
    	Content.JsonContent <| fun ctx ->
        	{ author = "loic.denuziere"; (* ... *) }
    | PostArticle articleData ->
    	Content.JsonContent <| fun ctx ->
        	SaveArticle articleData

Want to see a full example? How about a full CRUD API serving an in-memory database of people information, with all interactions perfectly type-safe, in less than fifty lines?

Look for this new WebSharper 3.0 pre-release on NuGet early next week, or build it right now from source!

By Loïc Denuzière on Wednesday, February 11, 2015 — 0 comments

CloudSharper 0.9.27 released Core team

Here comes another release of CloudSharper! Version 0.9.27 comes with a number of enhancements:

  • It is now possible to write RPC functions in F# interactive. When you call an [<Rpc>]-annotated function from the client side, the arguments are serialized and sent through the WebSocket used by F# Interactive, passed to the function, and the response is sent back the same way.
    Here is an example where two separate widgets share a common server-side state:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    let serverSideCounter = ref 0
    
    [<Rpc>]
    let increment() =
        serverSideCounter := !serverSideCounter + 1
    
    [<Rpc>]
    let get() =
        async { return !serverSideCounter }
    
    let x =
        <@ Button [Text "Increment"]
            |>! OnClick (fun _ _ -> increment()) @>
    
    let y =
        <@ Button [Text "Get the current value"]
            |>! OnClick (fun button _ ->
                async {
                    let! value = get()
                    button.Text <- "The value is " + string value
                }
                |> Async.Start) @>
  • The first time experience is largely improved. When entering CloudSharper without a local component running, instead of a message that could easily be mistaken for an error, CloudSharper now displays a friendly screen with install instructions.
  • The project templates are updated to the latest WebSharper (3.0.26-alpha).

We are now planning a major rework of the dashboard screen which will contain a lot of cool new features, including GitHub integration and statistics about your workspaces. We can't wait to have them ready for you to enjoy!

Happy coding!

By András Jankó on Tuesday, February 10, 2015 — 0 comments

WebSharper.3.0.26-alpha released Core team

This release of WebSharper 3.0 alpha ships several major and breaking changes: introducing strongly-typed JavaScript function interoperability, various WebSharper Interface Generator (WIG) API enhancements and revisions, bringing the JQuery binding up to date (to 1.11.2) and more type-safe, and lots of other small fixes and improvements.

If you update WebSharper, be sure to update all your other WebSharper-based packages and rebuild all your projects with the new version because of metadata incompatibility.

Strongly-typed JavaScript function interoperability

Summary of the visible changes

This change standardizes the way functions passed to and from JavaScript libraries (eg. callbacks) are represented. Those used to be inconsistent depending on whether the function was expected to use the this value: a delegate was used if it was, and an F# function with tupled arguments if it wasn't. Now the type of JavaScript callbacks is as follows:

1
fun this (arg1, arg2, ..., argn) -> result

If the function is not expected to use the this value, then the first argument is omitted, and the function is identical to previous versions of WebSharper. Conversely, if this is used and the JavaScript function takes no argument, then the second argument is omitted.

If the JavaScript function is variadic (ie. it takes n fixed arguments and a variable number of extra arguments), then the corresponding F# function takes a tuple of n + 1 arguments, where the last argument is an array of the variable arguments.

In some rare cases (eg. when using arrays of functions), the function needs to be wrapped in one of the types described below in "New function types".

In-depth explanation

The FSharpFunc<'TArg, 'TRes> type (which is used for lambdas by the F# compiler) always take one parameter (which can be a tuple). In WebSharper translation, these become JavaScript functions taking a single fixed-length array (although curried functions only used in local scope are optimized). However, it is often required that we pass functions defined in F# to a JavaScript library (for example event handlers, callbacks, functional-style libraries).

Previous versions of WebSharper used a wrapper function called Runtime.Tupled for this. This created a function that passed along its arguments to the inner function if it was called with 0 or 1 arguments, otherwise passing the arguments object. This meant that Runtime.Tupled-wrapped functions had a double calling convention: with a single array or with multiple arguments.

Although this solution was a performance hit (tuple-argument lambdas got wrapped even if they were never passed to outside JavaScript code), it covered most cases. But if the JavaScript library would sometimes call the function with one argument and sometimes more, Runtime.Tupled would cause errors. Also there was no strongly typed way to handle functions returned by outside JavaScript libraries, or function-typed properties on objects.

This release introduces a one-to-one relation between new F# types describing functions and how they are to be called/treated in JavaScript. WIG is also expanded with support for interop transformations, including automatically converting between function calling conventions. F#-defined lambdas used only in passing to outside JavaScript code are now optimized in the output to be straight functions with multiple arguments, not a wrapped function. This means then a lambda like fun (a, b) -> a + b would be translated to function(tupledArg) { return tupledArg[0] + tupledArg[1]; } but if you have this lambda passed to a JavaScript library expecting a function with two arguments, it will become function(a, b) { return a + b; }.

New function types

In the IntelliFactory.WebSharper.JavaScript namespace you can find several new types corresponding to JavaScript functions. These have constructors for creating them from F# functions, and methods to call them. As described in the WIG section, in most cases these types are not needed, because WIG is creating inlines which convert to and from F# functions. However if you need to pass an array of JavaScript functions, it will use one of these types as automatically mapping an array would be confusing (arrays are mutable, we need to preserve identity).

  • For JavaScript functions with 0 or 1 parameters, that do not use the this value, simply use F# functions ('a -> 'b).
  • FuncWithArgs<'TArgs, 'TResult>: Represents a JavaScript function with more than one argument. The 'TArgs type parameter must be a tuple.
  • FuncWithThis<'TThis, 'TFunc>: Represents a JavaScript function that cares about the this value. 'TFunc can be a straight F# function or other interop type.
  • FuncWithRest<..., 'TRest, 'TResult>: Represents a JavaScript function that takes some fixed arguments, and then rest arguments. The number of fixed arguments can be between 0 and 6.
  • FuncWithArgsRest<'TArgs, 'TRest, 'TResult>: This is for covering the probably rare case of more than 6 fixed arguments. 'TArgs must be a tuple type of the fixed arguments. Its .Call method also takes the fixed arguments as a tuple.
  • Delegate types are no longer supported for creating JavaScript functions using the this argument. Only explicitly proxied delegate types are translated. (WebSharper currently only defines proxy for System.Action which is used by CancellationToken.Register.)

Interface Generator API changes

  • We felt that using |+> [...] and |+> Protocol [...] to add static and instance members accordingly is a bit non-intuitive, and also an easy source of errors, because forgetting Protocol would give no warning. Adding a list of members to a type definition is now recommended using |+> Static [...] and |+> Instance [...]. Previous syntax will give deprecated warning. Also: constructors can be in the Instance list and they will still get handled correctly.
  • Adding members to an interface is still done with |+> [...].
  • There have been functions called Static and Instance operating on a single method definition. These would have no use now and have been removed.
  • You can use the TSelf value as a placeholder in member declarations and will be resolved to the declaring type.
  • The WithMacro helper adds a WebSharper macro to a method/constructor. Currently the macro type itself must be defined in another assembly than the WIG generator assembly.
  • ObjectConstructor creates a constructor where the inline is a simple JavaScript object expression. This functionality was previously exposed only by the Pattern.Config helper.
  • Generic type definitions also use Generic – now instead of Generic /, and produce a single CodeModel.TypeDefinition value. This can be directly inserted into CodeModel.Namespace member lists. When you want to add specify type arguments, use the item syntax MyTypeDef.[T1, T2, ...] as you can with other types. The number of applied type parameters is not checked by the type system currently, but will give a detailed error when compiling with WIG. This includes wrongly parameterizing external types which would previously generate invalid IL.
  • Use Generic + ["T1"; "T2"] - ... to specify the names of the type parameters.
  • Generic – now takes a function with arguments of type CodeModel.TypeParameter. In some cases, explicitly declaring the argument type can be required for type inference. Also, sometimes using p.Type to convert a CodeModel.TypeParameter to a Type.Type is needed.
  • There is a new overload of Generic – which takes an int * (list<CodeModel.TypeParameter> -> #CodeModel.Entity), where the first value is the number of type parameters. This allows creating generic type declarations or members with more than 4 type parameters.
  • You can now set type constraints on parameters using p.Constraints <- [...] inside the lambda passed to Generic -. Previous WithConstraints helper is removed as we want to have all helper functions named With... to be non-destructive.
  • You can add a custom defined inline transformation with the WithInterop helper. This takes a record with an In and an Out field, both string -> string. For example:

    1
    2
    3
    4
    5
    
    let Index = 
        T<int> |> WithInterop { 
            In  = fun s -> s + " - 1"
            Out = fun s -> s + " + 1"
        }

    You can then use this Type.Type value instead of T<int> in your member declarations where you want to handle an index as 1-based in your code, but pass it to and get it from a 0-based value in a JavaScript library. On method parameters and property setters the In function will be used on the parameter or property value in the automatic inline. On method return values and property getters the Out function will be used on the whole inline of the method or property getter.

  • You can use the WithNoInterop helper to (non-destructively) clean any automatic and custom inline transformations from a Type.Type value.
  • To define a custom inline for a method that still makes use of the default or custom inline transformations on parameters and return value, use the WithInteropInline helper. It takes a function typed (string -> string) -> string, use the provided function on a parameter name or index to get its transformed inline. For example, defining an on event setter with function argument detupling:

    1
    2
    
    "onChange" => (T<int> * T<obj> ^-> T<unit>)?callback ^-> T<unit>
    |> WithInteropInline (fun tr -> "$this.on('change', " + tr "callback" + ")"
  • Similar helpers exists for property getters and setters: WithInteropGetterInline and WithInteropSetterInline. For getters, provided function is only usable for transforming "index" in the case of an indexed property, and for setters transforming "value" or "index".
  • You can now use the !? operator on types too, not just parameters. Existing method definitions work without change as before, producing method overloads of different arity. On properties the option type is converted to and from an existing or missing field on a JavaScript object. On method returns, undefined is converted to None, all other values (including null) to Some ....
  • Union types (for example T<int> + T<string>) was used previously for creating method overloads, but defaulted to T<obj> when used on a property or method return type. Now these get automatically transformed to Choice types when the cases can be distinguished in JavaScript using the typeof, Array.isArray and the arr.length functions. In F# this means either at most one array case or possibly multiple tuple cases with all different length, at most one number type (including DateTime and TimeSpan, which are proxied as a Number), string, bool, and at most one other object type. If there are cases which can't be separated, the type will still default to obj.

Other improvements

  • The IntelliFactory.WebSharper.JavaScript.Object type is now generic. Object<obj> is equivalent to previous Object type. Now, if relevant, you can specify the types of all fields, for example Object<string> for a JavaScript string map.
  • You can use the [<OptionalField>] attribute on a class or record field with an option type to represent it with an existing/nonexisting field in the JavaScript translation. (Planned: [<OptionalField>] on a class or record will apply to all fields with an option type.)
  • The [<Name "">] attribute now works on class fields.
  • JQuery binding updated with better typing for AJAX operations.

Fixes

  • WIG couldn't create tuple types with length more than 7.
  • Bug #323 Derived classes defining a field with the same name as a base class don't overlap in JavaScript translation.
  • Bug #330 withCredentials should not be used in synchronous requests. Thanks to ca-ta for the report and pull request.

Known issues

  • Currently the TypeScript definition output can be off from the generated JavaScript as it was not reviewed yet with the latest changes.
  • Source mapping at the current state is not as usable as we would like. Many-to-many mapping is not handled well by either IE11 or Chrome.

By Loïc Denuzière on Thursday, January 29, 2015 — 0 comments

WebSharper 3.0.15-alpha released Core team

Here comes another release of WebSharper 3.0 alpha! This release focuses on small fixes and improvements, as well as a couple new proxies for useful classes such as System.Random and System.Guid.

Changes

Improvements:

  • WIG: The (?) operator for naming a method argument now properly overrides the JavaScript field access operator. This means that you can now open IntelliFactory.WebSharper.JavaScript in a WIG project, provided that you do it before open IntelliFactory.WebSharper.InterfaceGenerator.
  • Added xml documentation for attributes and sitelets.
  • Added Sitelets.UserSession.LoginPersistent to create login sessions that persist beyond the current browser session.
  • Added client-side Remoting.UseHttps() which forces all RPC calls to use HTTPS.

New proxies:

The following classes and methods can now be used from the client side.

  • System.Random
  • System.Guid
  • System.String.TrimStart/End

Fixes:

  • Fix #315: Sitelet.Protect login redirect now uses RedirectTemporary instead of Redirect.
  • Fix #322: Compile-time error when a project's Name and AssemblyName are different.
  • Fix #323: Fields for primary constructor arguments shadow inherited ones
  • Fix #325: Add Console.Log overload with a single argument

Future plans

We have two final major changes to push for WebSharper 3.0, which affect the typing conventions for tupled and multiple argument functions (see the work in progress) and the interface generator API. Our current plan is to have another alpha release for this (and other, smaller changes), and ideally to release the first stable version of WebSharper 3.0 before the end of February.

Happy coding!

By Loïc Denuzière on Tuesday, January 13, 2015 — 0 comments

CloudSharper 0.9.26 released Core team

In this new release of CloudSharper, we added a new HTTP API that allows you to create a workspace in a single click. For example, if you are the owner of an F# library, you can add a "Try in CloudSharper" link to your website which will direct the users to CloudSharper and create a project including the source files and NuGet packages that you specified. Here are some more details about the API. Sharing has never been easier!

We also updated the API references available in the Documentation panel or by pressing F1 on an autocomplete item.

Happy coding!

By Loïc Denuzière on Friday, January 9, 2015 — 0 comments

CloudSharper 0.9.25 released Core team

We just released a new version of CloudSharper alpha, 0.9.25. This version updates the CloudSharper interface and the project templates to the latest version of WebSharper 3.0.

You can of course keep working on projects using older versions of WebSharper. If you want to update your projects, simply update the NuGet packages for WebSharper and any extensions you are using, and follow this guide to upgrade your code.

Happy coding!

By Loïc Denuzière on Thursday, January 8, 2015 — 1 comment

WebSharper 3.0.8-alpha published Core team

As we advance closer to the stable release of WebSharper 3.0, more and more of the breaking changes we have wanted to implement for a long time are coming out. The main theme of the just-released version 3.0.8-alpha is naming conventions streamlining. With these changes, WebSharper should be more intuitive to use than ever. The changes necessary to update an existing project are fairly simple and detailed in the comprehensive guide below.

Naming changes

In names below, IF is short for IntelliFactory and IFWS for IntelliFactory.WebSharper.

JavaScript standard library

Until now, the JavaScript standard library was split into a number of namespaces and modules, making it sometimes difficult to find where something was. The new namespace IFWS.JavaScript is now the home of everything built in the browser, whether it's basic language constructs, DOM, HTML5, etc.

The general rule is:
If a module contains client-side code, you should open IntelliFactory.WebSharper.JavaScript.
If it only contains server-side code, you should not.

For the purpose of the above rule, WIG definitions ("Extension" projects) are not considered client-side code, since they are run during compilation to generate inlines. In fact, it is important not to open IFWS.JavaScript in a WIG definition because of a conflict on the (?) operator.

In more detail:

  • IFWS.EcmaScript and IFWS.Html5 are removed and all types defined under them are now under IFWS.JavaScript. The module IFWS.EcmaScript.Global is merged with the old IFWS.JavaScript and renamed to IFWS.JavaScript.JS. A few HTML5 items have also been cleaned up, and more will certainly come in the future. For example, code that used to read:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    open IntelliFactory.WebSharper
    
    let a = EcmaScript.Date.Now()
    let b = new Html5.Float32Array()
    let c = Global.NaN
    let d = JavaScript.Undefined
    let e : EcmaScript.WebGLRenderingContext = (* ... *)
    let f : Html5.Position = (* ... *)
    JavaScript.Log "Some text on the console"

    now reads:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    open IntelliFactory.WebSharper
    open IntelliFactory.WebSharper.JavaScript
    
    let a = Date.Now()
    let b = new Float32Array()
    let c = JS.NaN
    let d = JS.Undefined
    let e : WebGL.RenderingContext = (* ... *)
    let f : Geolocation.Position = (* ... *)
    Console.Log "Some text on the console"

    JS now basically contains all top-level values and language constructs defined in JavaScript. This includes JS.Window and JS.Document, which obsolete Html5.Window.Self and Dom.Document.Current, respectively.

  • IFWS.Dom is renamed to IFWS.JavaScript.Dom.
  • The auto-opened IFWS.Pervasives is split in two:

    • IFWS.Pervasives contains definitions useful for both client-side and server-side code, such as attributes ([<JavaScript>], [<Inline>], [<Rpc>], etc).
    • IFWS.JavaScript.Pervasives contains client-side definitions, such as:

      1
      2
      3
      4
      5
      6
      7
      8
      
      open IntelliFactory.WebSharper.JavaScript
      
      // notation for plain JavaScript object creation
      let g = New ["x" => 12; "y" => 14]
      // plain JavaScript field access
      let h = e?x
      // "dotted" operators, inlined to the corresponding JavaScript operator
      if g ===. h then ()

HTML combinators

  • The namespaces IF.Html and IFWS.Html have been a constant source of confusion for beginners. In WebSharper 3.0.8, they have been renamed to IFWS.Html.Server and IFWS.Html.Client, respectively. Hopefully this should make it more obvious when one or the other is needed.
  • Tag and attribute combinators that were previously under a HTML5 module are now grouped back together with the "normal" ones. We judged that it doesn't make sense to separate them out anymore.
  • The type IF.Html.Element<'T> was originally intended to allow embedding various types of client-side controls. But ultimately, only Web.Control has ever been used for this purpose. Therefore, we made the corresponding new IFWS.Html.Server.Element and all associated types (such as INode and Attribute) non-parametric.

Formlets

The following namespaces have been renamed for consistency with Sitelets and Piglets:

  • IF.Formlet --> IF.Formlets
  • IFWS.Formlet --> IFWS.Formlets
  • IFWS.Formlet.JQueryUI --> IFWS.Formlets.JQueryUI (extension)
  • IFWS.Formlet.TinyMce --> IFWS.Formlets.TinyMce (extension)

The NuGet package WebSharper.Formlet.JQueryUI has also been renamed to WebSharper.Formlets.JQueryUI.

Functionality changes

Web.Control and HTML type hierarchy

In preparation for an upcoming tighter integration of UI.Next, we changed the behavior of Web.Control. Namely, the Body property doesn't have the type IPagelet anymore, but a new interface IControlBody that is implemented by pagelets and will be soon implemented by UI.Next Docs. As a consequence:

  • The interface IPagelet is replaced by an abstract class Pagelet, which implements IControlBody appropriately.
  • The Body property of Html.Client.Elements now has type Dom.Node instead of Dom.Element. Some methods (such as JQuery.Append) require a Dom.Element; in this case, replace .Body with .Dom:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    // old code
    open IntelliFactory.WebSharper.Html
    open IntelliFactory.WebSharper.JQuery
    
    let e = Div [Text "some content"]
    JQuery.Of("#myElt").Append(e.Body).Ignore
    
    // new code
    open IntelliFactory.WebSharper.Html.Client
    open IntelliFactory.WebSharper.JQuery
    
    let e = Div [Text "some content"]
    JQuery.Of("#myElt").Append(e.Dom).Ignore

Assembly referencing

This one gets a bit more technical and is related to the WebSharper build process. WebSharper contains a custom MSBuild task written in F#, which is in charge of compiling code to JavaScript and extracting the compiled JavaScript code into a web project, among other things. Until now, this task was also in charge of adding all the WebSharper assembly references to the project just before running the F# compiler. This had several unfortunate consequences: the solution explorer in Visual Studio did not show the WebSharper references, and in some cases on Mono (such as when compiling a full solution in CloudSharper), XBuild would fail to add the references correctly. In this new release, we switched to a more conventional scheme where references are added to the project file itself by nuget install. This more standard behavior should harbor fewer opportunities for problems.

Conclusion

Phew, that was a long one! Overall these changes are fairly simple to apply to an existing project, and we are confident that they remove a number of idiosyncrasies that hinder the learning of WebSharper for beginners.

If you encounter any problems porting a project to the latest WebSharper, don't hesitate to tell us on the issue tracker. Happy coding!