CSS

It's high time we added some CSS styles to our HTML markup. We'll not deep-dive into the details about the styles itself, as this is not a tutorial on Web Design. The stylesheet can be downloaded from here in its final shape. Place the Site.css stylesheet in the root directory of the project, and manually add following to the SuaveMusicStore.fsproj, inside <ItemGroup> element:

<Content Include="Site.css">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

Note: Specifying CopyToOutputDirectory element with PreserveNewest value makes sure the stylesheet is accessible after the program has been compiled.

In order to include the stylesheet in our HTML markup, let's add the following to our View:

View.fs

   5: 
   6: 
   7: 
   8: 
   9: 
  10: 
  11: 
  12: 
let cssLink href = link [ "href", href; " rel", "stylesheet"; " type", "text/css" ] let index = html [] [ head [] [ title [] "Suave Music Store" cssLink "/Site.css" ]

This enables us to output the link HTML element with href attribute pointing to the CSS stylesheet.

The CSS depends on logo.png asset, which can be downloaded from here.

Again, place the logo.png in root directory, and add an entry to fsproj:

<Content Include="logo.png">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

A browser, when asked to include a CSS file, sends back a request to the server with the given url. In similar fashion, when the browser wants to render an image asset, it needs to GET it from the server.

If we have a look at our main WebPart we'll notice that there's really no handler capable of serving this file. That's why we need to add another alternative to our choose WebPart:

App.fs

  23: 
pathRegex "(.*)\.(css|png)" >=> Files.browseHome

The pathRegex WebPart returns Some if an incoming request specifies path matching the regular expression pattern. If that's the case, the Files.browseHome WebPart will be applied. The given pattern matches every file with either .css or .png extension, which protects us from accessing other (e.g. binary or config) files. Files.browseHome is a WebPart from Suave that serves static files from the root application directory.

Now you should be able to see the styles applied to our HTML markup.

type IntPath = PrintfFormat<(int -> string),unit,string,string,int>

Full name: SuaveMusicStore.Path.IntPath
Multiple items
type PrintfFormat<'Printer,'State,'Residue,'Result> =
new : value:string -> PrintfFormat<'Printer,'State,'Residue,'Result>
member Value : string

Full name: Microsoft.FSharp.Core.PrintfFormat<_,_,_,_>

--------------------
type PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple> =
inherit PrintfFormat<'Printer,'State,'Residue,'Result>
new : value:string -> PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple>

Full name: Microsoft.FSharp.Core.PrintfFormat<_,_,_,_,_>

--------------------
new : value:string -> PrintfFormat<'Printer,'State,'Residue,'Result>

--------------------
new : value:string -> PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple>
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
val home : string

Full name: SuaveMusicStore.Path.home
module Store

from SuaveMusicStore.Path
val overview : string

Full name: SuaveMusicStore.Path.Store.overview
val browse : string

Full name: SuaveMusicStore.Path.Store.browse
val details : IntPath

Full name: SuaveMusicStore.Path.Store.details
namespace Suave
module Html

from Suave
val cssLink : href:string -> Node

Full name: SuaveMusicStore.View.cssLink
val href : string
val link : attr:Attribute list -> Node

Full name: Suave.Html.link
val index : string

Full name: SuaveMusicStore.View.index
val html : (Attribute list -> Node list -> Node)

Full name: Suave.Html.html
val head : (Attribute list -> Node list -> Node)

Full name: Suave.Html.head
val title : attr:Attribute list -> s:string -> Node

Full name: Suave.Html.title
val body : (Attribute list -> Node list -> Node)

Full name: Suave.Html.body
val div : (Attribute list -> Node list -> Node)

Full name: Suave.Html.div
val tag : tag:string -> attr:Attribute list -> contents:Node list -> Node

Full name: Suave.Html.tag
val a : href:string -> attr:Attribute list -> (Node list -> Node)

Full name: Suave.Html.a
module Path

from SuaveMusicStore
union case Node.Text: string -> Node
val htmlToString : node:Node -> string

Full name: Suave.Html.htmlToString
module App

from SuaveMusicStore
module Filters

from Suave
module Operators

from Suave
module RequestErrors

from Suave
module Successful

from Suave
module Web

from Suave
val browse : (HttpContext -> Async<HttpContext option>)

Full name: SuaveMusicStore.App.browse
val request : apply:(HttpRequest -> HttpContext -> 'a) -> context:HttpContext -> 'a

Full name: Suave.Http.request
val r : HttpRequest
member HttpRequest.queryParam : key:string -> Choice<string,string>
union case Choice.Choice1Of2: 'T1 -> Choice<'T1,'T2>
val genre : string
val OK : body:string -> WebPart

Full name: Suave.Successful.OK
val sprintf : format:Printf.StringFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
union case Choice.Choice2Of2: 'T2 -> Choice<'T1,'T2>
val msg : string
val BAD_REQUEST : body:string -> WebPart

Full name: Suave.RequestErrors.BAD_REQUEST
val webPart : WebPart<HttpContext>

Full name: SuaveMusicStore.App.webPart
val choose : options:WebPart<'a> list -> WebPart<'a>

Full name: Suave.WebPart.choose
val path : pathAfterDomain:string -> WebPart

Full name: Suave.Filters.path
module View

from SuaveMusicStore
val pathScan : pf:PrintfFormat<'a,'b,'c,'d,'t> -> h:('t -> WebPart) -> WebPart

Full name: Suave.Filters.pathScan
val details : Path.IntPath

Full name: SuaveMusicStore.Path.Store.details
val id : int
val pathRegex : pathAfterDomainRegex:string -> WebPart

Full name: Suave.Filters.pathRegex
module Files

from Suave
val browseHome : WebPart

Full name: Suave.Files.browseHome
val startWebServer : config:SuaveConfig -> webpart:WebPart -> unit

Full name: Suave.Web.startWebServer
val defaultConfig : SuaveConfig

Full name: Suave.Web.defaultConfig

Show code from this section on GitHub

results matching ""

    No results matching ""