We've seen how to define basic routing in a Suave application.
In this section we'll see how we can deal with returning good looking HTML markup in a HTTP response.
Templating HTML views is quite a big topic itself, that we don't want to go into much details about.
Keep in mind that the concept can be approached in many different ways, and the way presented here is not the only proper way of rendering HTML views.
Having said that, I hope you'll still find the following implementation concise and easy to understand.
In this application we'll use server-side HTML templating with the help of a separate Suave package called
Note: As of the time of writing,
Suave.Experimentalis a separate package. It's likely that next releases of the package will include breaking changes. It's also possible that the modules we're going to use from within the package will be extracted to the core Suave package.
To use the package, we need to take a dependency on the following NuGet (add to paket.dependencies):
nuget Suave.Experimental 2.2.1
Also as was the case with
Suave package, make sure to add
Suave.Experimental to paket.references file and invoke paket install.
Before we start defining views, let's organize our
App.fs source file by adding following line at the beginning of the file:
1: 2: 3:
The line means that whatever we define in the file will be placed in
Read here for more info about organizing and structuring F# code.
Now let's add a new file
View.fs to the project. With Ionide you can simply create the file and then having it open in editor, trigger
F#: Add Current File To Project command.
We need to move the newly created file just before the
App.fs in the *.fsproj project, e.g. by invoking twice following Ionide command:
F#: Move File Up
Now let's place following module definition at the very top:
1: 2: 3:
We'll follow this convention throughout the tutorial to have a clear understanding of the project structure.
Note: It's very important that the
View.fsfile comes before
fsproj. F# compiler requires the referenced items to be defined before their usage. At first glance, that might seem like a big drawback, however after a while you start realizing that you can have much better control of your dependencies. Read the following for further benefits of lack of cyclic dependencies in F# project.namespace Suavemodule Html
from Suavemodule App
from SuaveMusicStoremodule Filters
from Suavemodule Operators
from Suavemodule RequestErrors
from Suavemodule Successful
from Suavemodule Web
from Suaveval browse : (HttpContext -> Async<HttpContext option>)
Full name: SuaveMusicStore.App.browseval request : apply:(HttpRequest -> HttpContext -> 'a) -> context:HttpContext -> 'a
Full name: Suave.Http.requestval r : HttpRequestmember HttpRequest.queryParam : key:string -> Choice<string,string>union case Choice.Choice1Of2: 'T1 -> Choice<'T1,'T2>val genre : stringval OK : body:string -> WebPart
Full name: Suave.Successful.OKval sprintf : format:Printf.StringFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintfunion case Choice.Choice2Of2: 'T2 -> Choice<'T1,'T2>val msg : stringval BAD_REQUEST : body:string -> WebPart
Full name: Suave.RequestErrors.BAD_REQUESTval webPart : WebPart<HttpContext>
Full name: SuaveMusicStore.App.webPartval choose : options:WebPart<'a> list -> WebPart<'a>
Full name: Suave.WebPart.chooseval path : pathAfterDomain:string -> WebPart
Full name: Suave.Filters.pathval pathScan : pf:PrintfFormat<'a,'b,'c,'d,'t> -> h:('t -> WebPart) -> WebPart
Full name: Suave.Filters.pathScanval id : intval startWebServer : config:SuaveConfig -> webpart:WebPart -> unit
Full name: Suave.Web.startWebServerval defaultConfig : SuaveConfig
Full name: Suave.Web.defaultConfig
Show code from this section on GitHub