Form module

We can delete an album, so why don't we proceed to add album functionality now? It will require a bit more effort, because we actually need some kind of a form with fields to create a new album. Fortunately, there's a helper module in Suave library exactly for this purpose.

Note: Suave.Form module at the time of writing is still in Experimental package - just as Suave.Html which we're already using.

First, let's create a separate module Form to keep all of our forms in there (yes there will be more soon). Add the Form.fs file just before View.fs - both View and App module will depend on Form. As with the rest of modules, don't forget to follow our modules naming convention.

Now declare the first Album form:

Form.fs

   1: 
   2: 
   3: 
   4: 
   5: 
   6: 
   7: 
   8: 
   9: 
  10: 
  11: 
  12: 
  13: 
  14: 
  15: 
  16: 
  17: 
  18: 
module SuaveMusicStore.Form open Suave.Form type Album = { ArtistId : decimal GenreId : decimal Title : string Price : decimal ArtUrl : string } let album : Form<Album> = Form ([ TextProp ((fun f -> <@ f.Title @>), [ maxLength 100 ]) TextProp ((fun f -> <@ f.ArtUrl @>), [ maxLength 100 ]) DecimalProp ((fun f -> <@ f.Price @>), [ min 0.01M; max 100.0M; step 0.01M ]) ], [])

Album type contains all fields needed for the form. For the moment, Suave.Form supports following types of fields:

  • decimal
  • string
  • System.Net.Mail.MailAddress
  • Suave.Form.Password

Note: the int type is not supported yet, however we can easily convert from decimal to int and vice versa

Afterwards comes a declaration of the album form itself. It consists of list of "Props" (Properties), of which we can think as of validations:

  • First, we declared that the Title must be of max length 100
  • Second, we declared the same for ArtUrl
  • Third, we declared that the Price must be between 0.01 and 100.0 with a step of 0.01 (this means that for example 1.001 is invalid)

Those properties can be now used as both client and server side. For client side we will use the album declaration in our View module in order to output HTML5 input validation attributes. For server side we will use an utility WebPart that will parse the form field values from a request.

Note: the above snippet uses F# Quotations - a feature that you can read more about here. For the sake of tutorial, you only need to know that they allow Suave to lookup the name of a Field from a property getter.

To see how we can use the form in View module, add open Suave.Form to the beginning:

View.fs

   1: 
   2: 
   3: 
   4: 
module SuaveMusicStore.View open Suave.Form open Suave.Html

Next, add this block of code:

View.fs

  20: 
  21: 
  22: 
  23: 
  24: 
  25: 
  26: 
  27: 
  28: 
  29: 
  30: 
  31: 
  32: 
  33: 
  34: 
  35: 
  36: 
  37: 
  38: 
  39: 
  40: 
  41: 
  42: 
  43: 
  44: 
  45: 
  46: 
  47: 
  48: 
  49: 
  50: 
  51: 
  52: 
  53: 
type Field<'a> = { Label : string Html : Form<'a> -> Suave.Html.Node } type Fieldset<'a> = { Legend : string Fields : Field<'a> list } type FormLayout<'a> = { Fieldsets : Fieldset<'a> list SubmitText : string Form : Form<'a> } let renderForm (layout : FormLayout<_>) = form [ for set in layout.Fieldsets -> tag "fieldset" [] [ yield tag "legend" [] [Text set.Legend] for field in set.Fields do yield div ["class", "editor-label"] [ Text field.Label ] yield div ["class", "editor-field"] [ field.Html layout.Form ] ] yield submitInput layout.SubmitText ]

Above snippet is quite long but, as we'll soon see, we'll be able to reuse it a few times. The FormLayout types defines a layout for a form and consists of:

  • SubmitText that will be used for the string value of submit button
  • Fieldsets - a list of Fieldset values
  • Form - instance of the form to render

The Fieldset type defines a layout for a fieldset:

  • Legend is a string value for a set of fields
  • Fields is a list of Field values

The Field type has:

  • a Label string
  • Html - function which takes Form and returns Node (object model for HTML markup). It might seem cumbersome, but the signature is deliberate in order to make use of partial application

Note: all of above types are generic, meaning they can accept any type of form, but the form's type must be consistent with the FormLayout hierarchy.

renderForm is a reusable function that takes an instance of FormLayout and returns HTML object model:

  • it creates a form element
  • the form contains a list of fieldsets, each of which:
    • outputs its legend first
    • iterates over its Fields and
      • outputs div element with label element for the field
      • outputs div element with target input element for the field
  • the form ends with a submit button
    Multiple items
    namespace FSharp

    --------------------
    namespace Microsoft.FSharp
    Multiple items
    namespace FSharp.Data

    --------------------
    namespace Microsoft.FSharp.Data
    namespace FSharp.Data.Sql
    Multiple items
    type LiteralAttribute =
    inherit Attribute
    new : unit -> LiteralAttribute

    Full name: Microsoft.FSharp.Core.LiteralAttribute

    --------------------
    new : unit -> LiteralAttribute
    val ConnectionString : string

    Full name: SuaveMusicStore.Db.ConnectionString
    type Sql = SqlDataProvider<...>

    Full name: SuaveMusicStore.Db.Sql
    type SqlDataProvider

    Full name: FSharp.Data.Sql.SqlDataProvider


    <summary>Typed representation of a database</summary>
    <param name='ConnectionString'>The connection string for the SQL database</param>
    <param name='ConnectionStringName'>The connection string name to select from a configuration file</param>
    <param name='DatabaseVendor'> The target database vendor</param>
    <param name='IndividualsAmount'>The amount of sample entities to project into the type system for each SQL entity type. Default 1000.</param>
    <param name='UseOptionTypes'>If true, F# option types will be used in place of nullable database columns. If false, you will always receive the default value of the column's type even if it is null in the database.</param>
    <param name='ResolutionPath'>The location to look for dynamically loaded assemblies containing database vendor specific connections and custom types.</param>
    <param name='Owner'>The owner of the schema for this provider to resolve (Oracle Only)</param>
    <param name='CaseSensitivityChange'>Should we do ToUpper or ToLower when generating table names?</param>
    <param name='TableNames'>Comma separated table names list to limit a number of tables in big instances. The names can have '%' sign to handle it as in the 'LIKE' query (Oracle and MSSQL Only)</param>
    <param name='OdbcQuote'>Odbc quote characters: Quote characters for the table and column names: `alias`, [alias]</param>
    <param name='SQLiteLibrary'>Use System.Data.SQLite or Mono.Data.SQLite or select automatically (SQLite only)</param>
    namespace FSharp.Data.Sql.Common
    type DatabaseProviderTypes =
    | MSSQLSERVER = 0
    | SQLITE = 1
    | POSTGRESQL = 2
    | MYSQL = 3
    | ORACLE = 4
    | MSACCESS = 5
    | ODBC = 6
    | FIREBIRD = 7

    Full name: FSharp.Data.Sql.Common.DatabaseProviderTypes
    Common.DatabaseProviderTypes.POSTGRESQL: Common.DatabaseProviderTypes = 2
    type CaseSensitivityChange =
    | ORIGINAL = 0
    | TOUPPER = 1
    | TOLOWER = 2

    Full name: FSharp.Data.Sql.Common.CaseSensitivityChange
    Common.CaseSensitivityChange.ORIGINAL: Common.CaseSensitivityChange = 0
    type DbContext = SqlDataProvider<...>.dataContext

    Full name: SuaveMusicStore.Db.DbContext
    type dataContext =
    member ClearUpdates : unit -> List<SqlEntity>
    member CreateConnection : unit -> IDbConnection
    member GetUpdates : unit -> List<SqlEntity>
    member Public : publicSchema
    member SubmitUpdates : unit -> Unit
    member SubmitUpdatesAsync : unit -> Async<Unit>
    nested type public.albumdetails.Individuals
    nested type public.albumdetailsEntity
    nested type public.albums.Individuals
    nested type public.albumsEntity
    ...

    Full name: FSharp.Data.Sql.SqlDataProvider,DatabaseVendor="2",ConnectionString="Server=192.168.99.100;Database=suavemusicstore;User Id=suave;Password=1234;",CaseSensitivityChange="0".dataContext
    type Album = SqlDataProvider<...>.dataContext.public.albumsEntity

    Full name: SuaveMusicStore.Db.Album
    type Genre = SqlDataProvider<...>.dataContext.public.genresEntity

    Full name: SuaveMusicStore.Db.Genre
    type AlbumDetails = SqlDataProvider<...>.dataContext.public.albumdetailsEntity

    Full name: SuaveMusicStore.Db.AlbumDetails
    val getContext : unit -> SqlDataProvider<...>.dataContext

    Full name: SuaveMusicStore.Db.getContext
    SqlDataProvider<...>.GetDataContext() : SqlDataProvider<...>.dataContext


    <summary>Returns an instance of the SQL Provider using the static parameters</summary>

    SqlDataProvider<...>.GetDataContext(transactionOptions: Transactions.TransactionOptions) : SqlDataProvider<...>.dataContext


    <summary>Returns an instance of the SQL Provider</summary>
    <param name='transactionOptions'>TransactionOptions for the transaction created on SubmitChanges.</param>

    SqlDataProvider<...>.GetDataContext(connectionString: string) : SqlDataProvider<...>.dataContext


    <summary>Returns an instance of the SQL Provider</summary>
    <param name='connectionString'>The database connection string</param>

    SqlDataProvider<...>.GetDataContext(connectionString: string, transactionOptions: Transactions.TransactionOptions) : SqlDataProvider<...>.dataContext


    <summary>Returns an instance of the SQL Provider</summary>
    <param name='connectionString'>The database connection string</param>
    <param name='transactionOptions'>TransactionOptions for the transaction created on SubmitChanges.</param>

    SqlDataProvider<...>.GetDataContext(connectionString: string, resolutionPath: string) : SqlDataProvider<...>.dataContext


    <summary>Returns an instance of the SQL Provider</summary>
    <param name='connectionString'>The database connection string</param>
    <param name='resolutionPath'>The location to look for dynamically loaded assemblies containing database vendor specific connections and custom types</param>

    SqlDataProvider<...>.GetDataContext(connectionString: string, resolutionPath: string, transactionOptions: Transactions.TransactionOptions) : SqlDataProvider<...>.dataContext


    <summary>Returns an instance of the SQL Provider</summary>
    <param name='connectionString'>The database connection string</param>
    <param name='resolutionPath'>The location to look for dynamically loaded assemblies containing database vendor specific connections and custom types</param>
    <param name='transactionOptions'>TransactionOptions for the transaction created on SubmitChanges.</param>
    val getGenres : ctx:DbContext -> Genre list

    Full name: SuaveMusicStore.Db.getGenres
    val ctx : DbContext
    type 'T list = List<'T>

    Full name: Microsoft.FSharp.Collections.list<_>
    property SqlDataProvider<...>.dataContext.Public: SqlDataProvider<...>.dataContext.publicSchema
    property SqlDataProvider<...>.dataContext.publicSchema.Genres: SqlDataProvider<...>.dataContext.publicSchema.public.genres


    <summary> The base table genres belonging to schema public</summary>
    Multiple items
    module Seq

    from FSharp.Data.Sql

    --------------------
    module Seq

    from Microsoft.FSharp.Collections
    val toList : source:seq<'T> -> 'T list

    Full name: Microsoft.FSharp.Collections.Seq.toList
    val getAlbumsForGenre : genreName:string -> ctx:DbContext -> Album list

    Full name: SuaveMusicStore.Db.getAlbumsForGenre
    val genreName : string
    val query : Linq.QueryBuilder

    Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.query
    val album : SqlDataProvider<...>.dataContext.public.albumsEntity
    property SqlDataProvider<...>.dataContext.publicSchema.Albums: SqlDataProvider<...>.dataContext.publicSchema.public.albums


    <summary> The base table albums belonging to schema public</summary>
    custom operation: join var in collection on (outerKey = innerKey). Note that parentheses are required after 'on'

    Calls Linq.QueryBuilder.Join
    val genre : SqlDataProvider<...>.dataContext.public.genresEntity
    property SqlDataProvider<...>.dataContext.public.genresEntity.Genreid: int


    <summary> integer</summary>
    custom operation: where (bool)

    Calls Linq.QueryBuilder.Where
    property SqlDataProvider<...>.dataContext.public.genresEntity.Name: string


    <summary> character varying(120)</summary>
    custom operation: select ('Result)

    Calls Linq.QueryBuilder.Select
    val getAlbumDetails : id:int -> ctx:DbContext -> AlbumDetails option

    Full name: SuaveMusicStore.Db.getAlbumDetails
    val id : int
    type 'T option = Option<'T>

    Full name: Microsoft.FSharp.Core.option<_>
    val album : SqlDataProvider<...>.dataContext.public.albumdetailsEntity
    property SqlDataProvider<...>.dataContext.publicSchema.Albumdetails: SqlDataProvider<...>.dataContext.publicSchema.public.albumdetails


    <summary> The view albumdetails belonging to schema public</summary>
    property SqlDataProvider<...>.dataContext.public.albumdetailsEntity.Albumid: int


    <summary> integer</summary>
    val tryHead : source:seq<'T> -> 'T option

    Full name: Microsoft.FSharp.Collections.Seq.tryHead
    val getAlbumsDetails : ctx:DbContext -> AlbumDetails list

    Full name: SuaveMusicStore.Db.getAlbumsDetails
    Multiple items
    module List

    from FSharp.Data.Sql

    --------------------
    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 sortBy : projection:('T -> 'Key) -> list:'T list -> 'T list (requires comparison)

    Full name: Microsoft.FSharp.Collections.List.sortBy
    val a : SqlDataProvider<...>.dataContext.public.albumdetailsEntity
    property SqlDataProvider<...>.dataContext.public.albumdetailsEntity.Artist: string


    <summary> character varying(120)</summary>
    val getAlbum : id:int -> ctx:DbContext -> Album option

    Full name: SuaveMusicStore.Db.getAlbum
    property SqlDataProvider<...>.dataContext.public.albumsEntity.Albumid: int


    <summary> integer</summary>
    val deleteAlbum : album:Album -> ctx:DbContext -> Unit

    Full name: SuaveMusicStore.Db.deleteAlbum
    val album : Album
    member Common.SqlEntity.Delete : unit -> unit
    SqlDataProvider<...>.dataContext.SubmitUpdates() : Unit


    <summary>Save changes to data-source. May throws errors: To deal with non-saved items use GetUpdates() and ClearUpdates().</summary>
    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 withParam : key:string * value:string -> path:string -> string

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

    Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
    val home : string

    Full name: SuaveMusicStore.Path.home
    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
    val browseKey : string

    Full name: SuaveMusicStore.Path.Store.browseKey
    module Admin

    from SuaveMusicStore.Path
    val manage : string

    Full name: SuaveMusicStore.Path.Admin.manage
    val deleteAlbum : IntPath

    Full name: SuaveMusicStore.Path.Admin.deleteAlbum
    namespace Suave
    module Form

    from Suave
    type Album =
    {ArtistId: decimal;
    GenreId: decimal;
    Title: string;
    Price: decimal;
    ArtUrl: string;}

    Full name: SuaveMusicStore.Form.Album
    Album.ArtistId: decimal
    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<_>
    Album.GenreId: decimal
    Album.Title: string
    Album.Price: decimal
    Album.ArtUrl: string
    val album : Form<Album>

    Full name: SuaveMusicStore.Form.album
    Multiple items
    union case Form.Form: FormProp<'a> list * ServerSideValidation<'a> list -> Form<'a>

    --------------------
    type Form<'a> = | Form of FormProp<'a> list * ServerSideValidation<'a> list

    Full name: Suave.Form.Form<_>
    union case FormProp.TextProp: Property<'a,string> -> FormProp<'a>
    val f : Album
    val maxLength : max:int -> Validation<string>

    Full name: Suave.Form.maxLength
    union case FormProp.DecimalProp: Property<'a,decimal> -> FormProp<'a>
    val min : min:decimal -> Validation<decimal>

    Full name: Suave.Form.min
    val max : max:decimal -> Validation<decimal>

    Full name: Suave.Form.max
    val step : step:System.Decimal -> Validation<decimal>

    Full name: Suave.Form.step
    module Html

    from Suave
    val em : s:string -> Node

    Full name: SuaveMusicStore.View.em
    val s : string
    val tag : tag:string -> attr:Attribute list -> contents:Node list -> Node

    Full name: Suave.Html.tag
    union case Node.Text: string -> Node
    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 h2 : s:string -> Node

    Full name: SuaveMusicStore.View.h2
    val ul : nodes:Node list -> Node

    Full name: SuaveMusicStore.View.ul
    val nodes : Node list
    val li : (Node list -> Node)

    Full name: SuaveMusicStore.View.li
    val table : x:Node list -> Node

    Full name: SuaveMusicStore.View.table
    val x : Node list
    val th : x:Node list -> Node

    Full name: SuaveMusicStore.View.th
    val tr : x:Node list -> Node

    Full name: SuaveMusicStore.View.tr
    val td : x:Node list -> Node

    Full name: SuaveMusicStore.View.td
    val strong : s:string -> Node

    Full name: SuaveMusicStore.View.strong
    val text : s:string -> Node list

    Full name: Suave.Html.text
    val form : x:Node list -> Node

    Full name: SuaveMusicStore.View.form
    val submitInput : value:string -> Node

    Full name: SuaveMusicStore.View.submitInput
    val input : attr:Attribute list -> Node

    Full name: Suave.Html.input
    type Field<'a> =
    {Label: string;
    Html: Form<'a> -> Node;}

    Full name: SuaveMusicStore.View.Field<_>
    Field.Label: string
    Multiple items
    Field.Html: Form<'a> -> Node

    --------------------
    type HtmlAttribute = string * string

    Full name: Suave.Form.HtmlAttribute
    Multiple items
    union case Form.Form: FormProp<'a> list * ServerSideValidation<'a> list -> Form<'a>

    --------------------
    module Form

    from SuaveMusicStore

    --------------------
    type Form<'a> = | Form of FormProp<'a> list * ServerSideValidation<'a> list

    Full name: Suave.Form.Form<_>
    type Node =
    | Element of Element * Node list
    | VoidElement of Element
    | Text of string
    | Raw of string
    | WhiteSpace of string

    Full name: Suave.Html.Node
    type Fieldset<'a> =
    {Legend: string;
    Fields: Field<'a> list;}

    Full name: SuaveMusicStore.View.Fieldset<_>
    Fieldset.Legend: string
    Fieldset.Fields: Field<'a> list
    type FormLayout<'a> =
    {Fieldsets: Fieldset<'a> list;
    SubmitText: string;
    Form: Form<'a>;}

    Full name: SuaveMusicStore.View.FormLayout<_>
    FormLayout.Fieldsets: Fieldset<'a> list
    FormLayout.SubmitText: string
    Multiple items
    FormLayout.Form: Form<'a>

    --------------------
    module Form

    from SuaveMusicStore

    --------------------
    type Form<'a> = | Form of FormProp<'a> list * ServerSideValidation<'a> list

    Full name: Suave.Form.Form<_>
    val renderForm : layout:FormLayout<'a> -> Node

    Full name: SuaveMusicStore.View.renderForm
    val layout : FormLayout<'a>
    val set : Fieldset<'a>
    val field : Field<'a>
    val div : (Attribute list -> Node list -> Node)

    Full name: Suave.Html.div
    Field.Html: Form<'a> -> Node
    FormLayout.Form: Form<'a>
    val home : Node list

    Full name: SuaveMusicStore.View.home
    val store : genres:string list -> Node list

    Full name: SuaveMusicStore.View.store
    val genres : string list
    val p : (Attribute list -> Node list -> Node)

    Full name: Suave.Html.p
    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
    val genre : string
    val url : string
    module Path

    from SuaveMusicStore
    module Store

    from SuaveMusicStore.Path
    val a : href:string -> attr:Attribute list -> (Node list -> Node)

    Full name: Suave.Html.a
    val browse : genre:string -> albums:Db.Album list -> Node list

    Full name: SuaveMusicStore.View.browse
    val albums : Db.Album list
    module Db

    from SuaveMusicStore
    type Album = FSharp.Data.Sql.SqlDataProvider<...>.dataContext.public.albumsEntity

    Full name: SuaveMusicStore.Db.Album
    val album : Db.Album
    val details : Path.IntPath

    Full name: SuaveMusicStore.Path.Store.details
    property FSharp.Data.Sql.SqlDataProvider<...>.dataContext.public.albumsEntity.Albumid: int


    <summary> integer</summary>
    property FSharp.Data.Sql.SqlDataProvider<...>.dataContext.public.albumsEntity.Title: string


    <summary> character varying(160)</summary>
    val details : album:Db.AlbumDetails -> Node list

    Full name: SuaveMusicStore.View.details
    val album : Db.AlbumDetails
    type AlbumDetails = FSharp.Data.Sql.SqlDataProvider<...>.dataContext.public.albumdetailsEntity

    Full name: SuaveMusicStore.Db.AlbumDetails
    property FSharp.Data.Sql.SqlDataProvider<...>.dataContext.public.albumdetailsEntity.Title: string


    <summary> character varying(160)</summary>
    val img : attr:Attribute list -> Node

    Full name: Suave.Html.img
    property FSharp.Data.Sql.SqlDataProvider<...>.dataContext.public.albumdetailsEntity.Albumarturl: string


    <summary> character varying(1024)</summary>
    val caption : string
    val t : string
    property FSharp.Data.Sql.SqlDataProvider<...>.dataContext.public.albumdetailsEntity.Genre: string


    <summary> character varying(120)</summary>
    property FSharp.Data.Sql.SqlDataProvider<...>.dataContext.public.albumdetailsEntity.Artist: string


    <summary> character varying(120)</summary>
    property FSharp.Data.Sql.SqlDataProvider<...>.dataContext.public.albumdetailsEntity.Price: decimal


    <summary> numeric</summary>
    System.Decimal.ToString() : string
    System.Decimal.ToString(provider: System.IFormatProvider) : string
    System.Decimal.ToString(format: string) : string
    System.Decimal.ToString(format: string, provider: System.IFormatProvider) : string
    val truncate : k:int -> s:string -> string

    Full name: SuaveMusicStore.View.truncate
    val k : int
    property System.String.Length: int
    System.String.Substring(startIndex: int) : string
    System.String.Substring(startIndex: int, length: int) : string
    val manage : albums:Db.AlbumDetails list -> Node list

    Full name: SuaveMusicStore.View.manage
    val albums : Db.AlbumDetails list
    val deleteAlbum : Path.IntPath

    Full name: SuaveMusicStore.Path.Admin.deleteAlbum
    property FSharp.Data.Sql.SqlDataProvider<...>.dataContext.public.albumdetailsEntity.Albumid: int


    <summary> integer</summary>
    val deleteAlbum : albumTitle:string -> Node list

    Full name: SuaveMusicStore.View.deleteAlbum
    val albumTitle : string
    val br : attr:Attribute list -> Node

    Full name: Suave.Html.br
    val notFound : Node list

    Full name: SuaveMusicStore.View.notFound
    val index : container:Node list -> string

    Full name: SuaveMusicStore.View.index
    val container : Node list
    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 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 html : container:Html.Node list -> WebPart

    Full name: SuaveMusicStore.App.html
    val container : Html.Node list
    val OK : body:string -> WebPart

    Full name: Suave.Successful.OK
    module View

    from SuaveMusicStore
    val index : container:Html.Node list -> string

    Full name: SuaveMusicStore.View.index
    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 getContext : unit -> FSharp.Data.Sql.SqlDataProvider<...>.dataContext

    Full name: SuaveMusicStore.Db.getContext
    val getAlbumsForGenre : genreName:string -> ctx:Db.DbContext -> Db.Album list

    Full name: SuaveMusicStore.Db.getAlbumsForGenre
    val browse : genre:string -> albums:Db.Album list -> Html.Node list

    Full name: SuaveMusicStore.View.browse
    union case Choice.Choice2Of2: 'T2 -> Choice<'T1,'T2>
    val msg : string
    val BAD_REQUEST : body:string -> WebPart

    Full name: Suave.RequestErrors.BAD_REQUEST
    val overview : (HttpContext -> Async<HttpContext option>)

    Full name: SuaveMusicStore.App.overview
    val warbler : f:('t -> 't -> 'u) -> 't -> 'u

    Full name: Suave.WebPart.warbler
    val getGenres : ctx:Db.DbContext -> Db.Genre list

    Full name: SuaveMusicStore.Db.getGenres
    val map : mapping:('T -> 'U) -> list:'T list -> 'U list

    Full name: Microsoft.FSharp.Collections.List.map
    val g : Db.Genre
    property FSharp.Data.Sql.SqlDataProvider<...>.dataContext.public.genresEntity.Name: string


    <summary> character varying(120)</summary>
    val store : genres:string list -> Html.Node list

    Full name: SuaveMusicStore.View.store
    val details : id:int -> WebPart

    Full name: SuaveMusicStore.App.details
    val getAlbumDetails : id:int -> ctx:Db.DbContext -> Db.AlbumDetails option

    Full name: SuaveMusicStore.Db.getAlbumDetails
    union case Option.Some: Value: 'T -> Option<'T>
    val details : album:Db.AlbumDetails -> Html.Node list

    Full name: SuaveMusicStore.View.details
    union case Option.None: Option<'T>
    val never : WebPart<'a>

    Full name: Suave.WebPart.never
    val manage : (HttpContext -> Async<HttpContext option>)

    Full name: SuaveMusicStore.App.manage
    val getAlbumsDetails : ctx:Db.DbContext -> Db.AlbumDetails list

    Full name: SuaveMusicStore.Db.getAlbumsDetails
    val manage : albums:Db.AlbumDetails list -> Html.Node list

    Full name: SuaveMusicStore.View.manage
    val deleteAlbum : id:int -> WebPart<HttpContext>

    Full name: SuaveMusicStore.App.deleteAlbum
    val ctx : FSharp.Data.Sql.SqlDataProvider<...>.dataContext
    val getAlbum : id:int -> ctx:Db.DbContext -> Db.Album option

    Full name: SuaveMusicStore.Db.getAlbum
    val choose : options:WebPart<'a> list -> WebPart<'a>

    Full name: Suave.WebPart.choose
    val GET : WebPart

    Full name: Suave.Filters.GET
    val deleteAlbum : albumTitle:string -> Html.Node list

    Full name: SuaveMusicStore.View.deleteAlbum
    val POST : WebPart

    Full name: Suave.Filters.POST
    val deleteAlbum : album:Db.Album -> ctx:Db.DbContext -> Unit

    Full name: SuaveMusicStore.Db.deleteAlbum
    module Redirection

    from Suave
    val FOUND : location:string -> WebPart

    Full name: Suave.Redirection.FOUND
    val webPart : WebPart<HttpContext>

    Full name: SuaveMusicStore.App.webPart
    val path : pathAfterDomain:string -> WebPart

    Full name: Suave.Filters.path
    val home : Html.Node list

    Full name: SuaveMusicStore.View.home
    val pathScan : pf:PrintfFormat<'a,'b,'c,'d,'t> -> h:('t -> WebPart) -> WebPart

    Full name: Suave.Filters.pathScan
    val pathRegex : pathAfterDomainRegex:string -> WebPart

    Full name: Suave.Filters.pathRegex
    module Files

    from Suave
    val browseHome : WebPart

    Full name: Suave.Files.browseHome
    val notFound : Html.Node list

    Full name: SuaveMusicStore.View.notFound
    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 ""