F# in web development: WebSharper - Cornerstone in web development: WebSharper ... which depends on...

Post on 22-May-2018

215 views 1 download

Transcript of F# in web development: WebSharper - Cornerstone in web development: WebSharper ... which depends on...

#devsum15

F# in web development: WebSharper

Michał Śliwoń

@mihcall m.sliwon@gmail.com

What is WebSharper?

• F# Web Development Framework• Abstractions, abstractions everywhere

• Ecosystem (~50 extensions)• Full-stack F#, not only F#->Javascript compiler• Developed by IntelliFactory (~270 projects, all in F#), eating

their dog food• Apache license

Features

• Client-Server apps• Type system across client and server

• Plumbing for client-server apps• Idiomatic abstractions

• Pagelets• Sitelets• Formlets• Flowlets• Piglets

• Typed wrappers for Javascript libraries

When to use WebSharper?

• You love F#• You’re probably NOT in love with Javascript

When it’s „probabaly” not advised to use WebSharper?• You love Javascript• You just want something to spit some nice Javascript code• You can’t figure out what’s this Functional Programming buzz

all about

Starting with WebSharper

• Visual Studio• Nuget• vsix installer (with Visual Studio Templates)

• Xamarin Studio• Add-in

• CloudSharper• Web IDE, collaboration platform (Cloud)• Executed on local machine

WebSharper Code Sample

• Client-server

ASP.NET Integration

• Client-side controls• ... with Remoting

• In ASPX Pages• In Razor Pages• or as Sitelet

OWIN Integration

• WebSharper.Owin.RemotingMiddleware• WebSharper.Owin.SiteletMiddleware

Sitelets

• primary way to create server-side content• Route requests• Generate HTML or JSON responses

• Sitelets routing• Mapping from URLs to actions• Mapping from actions to URLs• Mapping from actions to content

• Custom Attributes to Sitelet.Infer

open WebSharper.Sitelets

type Action =| Index| Stats of username: string| BlogArticle of id: int * slug: string

type MyWebsite() =interface IWebsite<Action> with

member this.Sitelet =Sitelet.Infer <| function

| Index ->// Content of the index pageContent.PageContent <| fun ctx ->

{ Page.Default withTitle = Some "Welcome!"Body = [H1 [Text "Index page"]] }

| Stats username ->// Content of the stats page, which depends on the usernameContent.PageContent <| fun ctx ->

{ Page.Default withBody = [Text ("Stats for " + username)] }

| BlogArticle (id, slug) ->// Content of the article page, which depends on id and slugContent.PageContent <| fun ctx ->

{ Page.Default withBody = [Text (sprintf "Article id %i, slug %s" id slug)] }

member this.Actions = []

[<assembly: WebsiteAttribute(typeof<MyWebsite>)>]do ()

Sitelet.Infer Custom Attributes

• [<Method("GET", "POST", ...)>]• [<CompiledName "string">]• [<Query("arg1", "arg2", ...)>]• [<Json "arg">]• [<FormData("arg1", "arg2", ...)>]• [<DateTimeFormat(string)>]• [<Wildcard>]

module Content =open Http

val PageContent : (Context<'Action> -> Page) -> Content<'Action>val PageContentAsync : (Context<'Action> -> Async<Page>) -> Content<'Action>

val WithTemplate : Template<'T> -> (Context<'Action> -> 'T) -> Content<'Action>val WithTemplateAsync : Template<'T> -> (Context<'Action> -> Async<'T>) -> Content<'Action>

val JsonContent : (Context<'Action> -> 'T) -> Content<'Action>val JsonContentAsync : (Context<'Action> -> Async<'T>) -> Content<'Action>

val CustomContent : (Context<'Action> -> Response) -> Content<'Action>val CustomContentAsync : (Context<'Action> -> Async<Response>) -> Content<'Action>

module Content =/// Permanently redirect to an action. (HTTP status code 301)val Redirect : 'Action -> Content<'Action>

/// Permanently redirect to a URL. (HTTP status code 301)val RedirectToUrl : string -> Content<'Action>

/// Temporarily redirect to an action. (HTTP status code 307)val RedirectTemporary : 'Action -> Content<'Action>

/// Temporarily redirect to a URL. (HTTP status code 307)val RedirectTemporaryToUrl : string -> Content<'Action>

HTML Combinators

• Server-sidelet myDiv = Div []// HTML: <div></div>

let myRule = HR []// HTML: <hr/>

let mySection = Section [HR []]// HTML: <section><hr/></section>

let myP = P [Class "paragraph"] -< [Text "This is a paragraph."]// HTML: <p class="paragraph">This is a paragraph.</p>

• Client-side

HTML Templates

• ${NameOfHole}• data-replace="NameOfHole"• data-hole="NameOfHole"

UI.Next

• Reactive Web Development

open WebSharper.UI.Nextopen WebSharper.UI.Next.Html

let basicExample() =

let rvContent = Var.Create ""let vUpperContent =

rvContent.View|> View.Map (fun t -> t.ToUpper())

Div [] [Doc.Input [] rvContentLabel [] [Doc.TextView vUpperContent]

]

Formletstype Person = {

Name: stringEmail: string

}

[<JavaScript>]let PersonFormlet : Formlet<Person> =

let nameF = Controls.Input "" |> Validator.IsNotEmpty "Empty name not allowed" |> Enhance.WithValidationIcon|> Enhance.WithTextLabel "Name"

let emailF = Controls.Input ""|> Validator.IsEmail "Please enter valid email address" |> Enhance.WithValidationIcon|> Enhance.WithTextLabel "Email"

Formlet.Yield (fun name email -> {Name = name; Email = email})<*> nameF<*> emailF|> Enhance.WithSubmitAndResetButtons|> Enhance.WithLegend "Add a New Person"|> Enhance.WithFormContainer

[<JavaScript>]let Main () =

Div [PersonFormlet]

Piglets

• Yes...

• Similar to Formlets but gives you control over HTML generation

Extensions

• Sencha Architect• jQuery• Kendo UI• Google Visualization• Google Maps• D3, Raphael, Dojo• Babylon.js• ...

Deployment options

• OWIN Containers• IIS

• Azure Websites

namespace Samples

open WebSharper

open WebSharper.Html.Client

open WebSharper.Google.Maps

[<JavaScript>]

module GoogleMaps =

open WebSharper.JavaScript

let Sample buildMap =

Div [Attr.Style "padding-bottom:20px; width:500px; height:300px;"]

|>! OnAfterRender (fun mapElement ->

let center = new LatLng(37.4419, -122.1419)

let options = new MapOptions(center, 8)

let map = new Google.Maps.Map(mapElement.Body, options)

buildMap map)

let SimpleMap() =

Sample <| fun (map: Map) ->

let latLng = new LatLng(37.4419, -122.1419)

let options = new MapOptions(latLng, 8)

map.SetOptions options

let SimpleDirections() =

Sample <| fun map ->

let directionsService = new DirectionsService()

let directionsDisplay = new DirectionsRenderer();

map.SetCenter(new LatLng(41.850033, -87.6500523))

map.SetZoom 7

map.SetMapTypeId MapTypeId.ROADMAP

let a = DirectionsRendererOptions()

directionsDisplay.SetMap(map)

let mapDiv = map.GetDiv()

let dirPanel = Div [ Attr.Name "directionsDiv"]

let j = WebSharper.JQuery.JQuery.Of(mapDiv)

j.After(dirPanel.Dom).Ignore

directionsDisplay.SetPanel dirPanel.Body

let calcRoute () =

let start = "chicago, il"

let destination = "st louis, mo"

let request = new DirectionsRequest(start, destination, TravelMode.DRIVING)

directionsService.Route(request, fun (result, status) ->

if status = DirectionsStatus.OK then

directionsDisplay.SetDirections result)

calcRoute ()

Other web development options in F#

• Suave.io (simple web development F# library)• Pure F# ASP.NET MVC• FunScript (F# to javascript converter)• Freya (functional-first web stack)• Frank (combinators for composing web applications)• ServiceStack• NancyFx• Canopy (web testing framework)

How much does it cost?

• Free – $ 0• Mobile - $ 25 / month• Insider Basic (individual) - $ 299 / year• Insider Pro (company) - $ 899 / year

+ Support/Consulting/Training options

Resources

• http://websharper.com/docs• „Expert F# 3.0” Don Syme, Adam Granicz, Antonio Cisternino

#devsum15

Thank you DevSum 2015!@mihcall

@mihcall m.sliwon@gmail.com