HTML markup

It's time to replace plain text placeholders in containers with meaningful content. First, define h2 in View module to output HTML header of level 2:

let h2 s = tag "h2" [] (text s)
val h2 : s:'a -> 'b

Full name: CDocument.h2
val s : 'a

and replace text with a new h2 in each of the 4 containers.

We'd like the "/store" route to output hyperlinks to all genres in our Music Store. Let's add a helper function in Path module, that will be responsible for formatting HTTP urls with a key-value parameter:

let withParam (key,value) path = sprintf "%s?%s=%s" path key value
val withParam : key:string * value:string -> path:string -> string

Full name: CDocument.withParam
val key : string
val value : string
val path : string
val sprintf : format:Printf.StringFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf

The withParam function takes a tuple (key,value) as its first argument, path as the second and returns a properly formatted url. A tuple (or a pair) is a widely used structure in F#. It allows us to group two values into one in an easy manner. Syntax for creating a tuple is as follows: (item1, item2) - this might look like a standard parameter passing in many other languages including C#. Follow this link to learn more about tuples.

Add also a string key for the url parameter "/store/browse" in Path.Store module:

module Store = let overview = "/store" let browse = "/store/browse"
module Store

from CDocument
val overview : string

Full name: CDocument.Store.overview
val browse : string

Full name: CDocument.Store.browse

We'll use it in App module:

let browse = request (fun r -> match r.queryParam Path.Store.browseKey with
val browse : obj

Full name: CDocument.browse

Now, add the following for working with the unordered list (ul) and list item (li) elements in HTML:

let ul xml = tag "ul" [] (flatten xml) let li = tag "li" []
val ul : xml:'a -> 'b

Full name: CDocument.ul
val xml : 'a
val li : obj

Full name: CDocument.li

flatten takes a list of Xml and "flattens" it into a single Xml object model. The actual container for store can now look like the following:

let store genres = [ h2 "Browse Genres" p [ text (sprintf "Select from %d genres:" (List.length genres)) ] ul [ for g in genres -> li (aHref (Path.Store.browse |> Path.withParam (Path.Store.browseKey, g)) (text g)) ] ]
val store : genres:'a -> 'b list

Full name: CDocument.store
val genres : 'a
val sprintf : format:Printf.StringFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
  interface IEnumerable
  interface IEnumerable<'T>
  member GetSlice : startIndex:int option * endIndex:int option -> 'T list
  member Head : 'T
  member IsEmpty : bool
  member Item : index:int -> 'T with get
  member Length : int
  member Tail : 'T list
  static member Cons : head:'T * tail:'T list -> 'T list
  static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>
val length : list:'T list -> int

Full name: Microsoft.FSharp.Collections.List.length

Things worth commenting in the above snippet:

  • store now takes a list of genres (again the type is inferred by the compiler)
  • the [ for g in genres -> ... ] syntax is known as "list comprehension". Here we map every genre string from genres to a list item
  • aHref inside list item points to the Path.Store.browse url with "genre" parameter - we use the Path.withParam function defined earlier

To use View.store from App module, let's simply pass a hardcoded list for genres like following:

path Path.Store.overview >=> html (View.store ["Rock"; "Disco"; "Pop"])


GitHub commit: 4116ee213ae519ad745f5882ba9eb62e1d4fea4f

Files changed:

  • App.fs (modified)
  • Path.fs (modified)
  • View.fs (modified)

results matching ""

    No results matching ""