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") ] ]
Full name: CDocument.editAlbum
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"
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") ] ]
Full name: Microsoft.FSharp.Core.Operators.truncate
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()
Full name: CDocument.updateAlbum
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
Full name: CDocument.editAlbum
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<_>
Full name: Microsoft.FSharp.Collections.List.map
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<_>
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<_>
and finally pathScan
in main choose
WebPart:
pathScan Path.Admin.editAlbum editAlbum
Comments to above snippets:
editAlbum
View looks very much the same as thecreateAlbum
. 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 ourAlbum
value, while keeping track on what has changed beforeSubmitUpdates()
warbler
is needed ineditAlbum
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)