Views
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 Suave.Experimental
.
Note: As of the time of writing,
Suave.Experimental
is 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:
App.fs
1: 2: 3: |
|
The line means that whatever we define in the file will be placed in SuaveMusicStore.App
module.
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:
View.fs
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.fs
file comes beforeApp.fs
infsproj
. 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