Update album

We have delete, we have create, so we're left with the update part only. This one will be fairly easy, as it's gonna be very similar to create (we can reuse the album form we declared in Form module).

editAlbum in View:

let editAlbum (album : Db.Album) genres artists = [ h2 "Edit" renderForm { Form = Form.album Fieldsets = [ { Legend = "Album" Fields = [ { Label = "Genre" Xml = selectInput (fun f -> <@ f.GenreId @>) genres (Some (decimal album.GenreId)) } { Label = "Artist" Xml = selectInput (fun f -> <@ f.ArtistId @>) artists (Some (decimal album.ArtistId))} { Label = "Title" Xml = input (fun f -> <@ f.Title @>) ["value", album.Title] } { Label = "Price" Xml = input (fun f -> <@ f.Price @>) ["value", formatDec album.Price] } { Label = "Album Art Url" Xml = input (fun f -> <@ f.ArtUrl @>) ["value", "/placeholder.gif"] } ] } ] SubmitText = "Save Changes" } div [ aHref Path.Admin.manage (text "Back to list") ] ]
val editAlbum : album:'a -> genres:'b -> artists:'c -> 'd list

Full name: CDocument.editAlbum
val album : 'a
val genres : 'b
val artists : 'c
union case Option.Some: Value: 'T -> Option<'T>
Multiple items
val decimal : value:'T -> decimal (requires member op_Explicit)

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

--------------------
type decimal = System.Decimal

Full name: Microsoft.FSharp.Core.decimal

--------------------
type decimal<'Measure> = decimal

Full name: Microsoft.FSharp.Core.decimal<_>

Path:

let editAlbum : IntPath = "/admin/edit/%d"
val editAlbum : string

Full name: CDocument.editAlbum

Link in manage in View (note we also add links to "Details" here):

for album in albums -> tr [ for t in [ truncate 25 album.Artist; truncate 25 album.Title; album.Genre; formatDec album.Price ] -> td [ text t ] yield td [ aHref (sprintf Path.Admin.editAlbum album.AlbumId) (text "Edit") text " | " aHref (sprintf Path.Store.details album.AlbumId) (text "Details") text " | " aHref (sprintf Path.Admin.deleteAlbum album.AlbumId) (text "Delete") ] ]
val album : obj
val truncate : value:'T -> 'T (requires member Truncate)

Full name: Microsoft.FSharp.Core.Operators.truncate
val sprintf : format:Printf.StringFormat<'T> -> 'T

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

updateAlbum in Db module:

let updateAlbum (album : Album) (artistId, genreId, price, title) (ctx : DbContext) = album.ArtistId <- artistId album.GenreId <- genreId album.Price <- price album.Title <- title ctx.SubmitUpdates()
val updateAlbum : album:'a -> artistId:'b * genreId:'c * price:'d * title:'e -> ctx:'f -> 'g

Full name: CDocument.updateAlbum
val album : 'a
val artistId : 'b
val genreId : 'c
val price : 'd
val title : 'e
val ctx : 'f

editAlbum WebPart in App module:

let editAlbum id = let ctx = Db.getContext() match Db.getAlbum id ctx with | Some album -> choose [ GET >=> warbler (fun _ -> let genres = Db.getGenres ctx |> List.map (fun g -> decimal g.GenreId, g.Name) let artists = Db.getArtists ctx |> List.map (fun g -> decimal g.ArtistId, g.Name) html (View.editAlbum album genres artists)) POST >=> bindToForm Form.album (fun form -> Db.updateAlbum album (int form.ArtistId, int form.GenreId, form.Price, form.Title) ctx Redirection.FOUND Path.Admin.manage) ] | None -> never
val editAlbum : id:'a -> 'b

Full name: CDocument.editAlbum
val id : 'a
val ctx : obj
union case Option.Some: Value: 'T -> Option<'T>
val album : obj
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 map : mapping:('T -> 'U) -> list:'T list -> 'U list

Full name: Microsoft.FSharp.Collections.List.map
Multiple items
val decimal : value:'T -> decimal (requires member op_Explicit)

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

--------------------
type decimal = System.Decimal

Full name: Microsoft.FSharp.Core.decimal

--------------------
type decimal<'Measure> = decimal

Full name: Microsoft.FSharp.Core.decimal<_>
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<_>
union case Option.None: Option<'T>

and finally pathScan in main choose WebPart:

pathScan Path.Admin.editAlbum editAlbum

Comments to above snippets:

  • editAlbum View looks very much the same as the createAlbum. The only significant difference is that it has all the filed values pre-filled.
  • in Db.updateAlbum we can see examples of property setters. This is the way SQLProvider mutates our Album value, while keeping track on what has changed before SubmitUpdates()
  • warbler is needed in editAlbum GET handler to prevent eager evaluation
  • but it's not necessary for POST, because POST needs to parse the incoming request, thus the evaluation is postponed upon the successful parsing.
  • after the album is updated, a redirection to manage is applied

Note: SQLProvider allows to change Album properties after the object has been instantiated - that's generally against the immutability concept that's propagated in the functional programming paradigm. We need to remember however, that F# is not pure functional programming language, but rather "functional first". This means that while it encourages to write in functional style, it still allows to use Object Oriented constructs. This often turns out to be useful, for example when we need to improve performance.

Pheeew, this section was long, but also very productive. Looks like we can already do some serious interaction with the application!


GitHub commit: 38976b29c4228799714d9cc1e1704e1fa3cf3b0e

Files changed:

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

results matching ""

    No results matching ""