{"action":"created","issue":{"url":"https://api.github.com/repos/giraffe-fsharp/Giraffe/issues/126","repository_url":"https://api.github.com/repos/giraffe-fsharp/Giraffe","labels_url":"https://api.github.com/repos/giraffe-fsharp/Giraffe/issues/126/labels{/name}","comments_url":"https://api.github.com/repos/giraffe-fsharp/Giraffe/issues/126/comments","events_url":"https://api.github.com/repos/giraffe-fsharp/Giraffe/issues/126/events","html_url":"https://github.com/giraffe-fsharp/Giraffe/issues/126","id":270410177,"number":126,"title":"Creating a Form Module using XmlViewEngine","user":{"login":"vtquan","id":1715430,"avatar_url":"https://avatars0.githubusercontent.com/u/1715430?v=4","gravatar_id":"","url":"https://api.github.com/users/vtquan","html_url":"https://github.com/vtquan","followers_url":"https://api.github.com/users/vtquan/followers","following_url":"https://api.github.com/users/vtquan/following{/other_user}","gists_url":"https://api.github.com/users/vtquan/gists{/gist_id}","starred_url":"https://api.github.com/users/vtquan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vtquan/subscriptions","organizations_url":"https://api.github.com/users/vtquan/orgs","repos_url":"https://api.github.com/users/vtquan/repos","events_url":"https://api.github.com/users/vtquan/events{/privacy}","received_events_url":"https://api.github.com/users/vtquan/received_events","type":"User","site_admin":false},"labels":[{"id":513574369,"url":"https://api.github.com/repos/giraffe-fsharp/Giraffe/labels/question","name":"question","color":"cc317c","default":true}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":4,"created_at":"2017-11-01T18:44:17Z","updated_at":"2018-02-21T04:21:45Z","closed_at":null,"author_association":"CONTRIBUTOR","body":"I have been trying to create something similar to [this](https://theimowski.gitbooks.io/suave-music-store/content/en/form_module.html) but using Giraffe's XmlViewEngine instead.\r\n\r\nYou can see how I port it over below but I am not satisfy with it for a few reasons\r\n\r\nThere is no validation since there is no Giraffe equivalent to Suave.Form. Of course, in Razor, we can use data annotation but not for XmlViewEngine. Creating types as in type driven development might work also but I don't know how it would work with ``BindForm()``\r\n\r\nI have to manually set the name of the input field. The tutorial was using Code Quotations for that but I wasn't able to get it to work. \r\n\r\nAlso I can't do a redirect in ``let createAlbum =``. I can't close the task without the ``return!`` and that would skip my ``redirectTo`` statement.\r\n\r\nI want to know how you would improve on this or would you approach this differently?\r\n\r\n```fsharp\r\ntype Field<'a> = {\r\n    Label : string\r\n    Html : XmlNode\r\n}\r\n\r\ntype Fieldset<'a> = {\r\n    Legend : string\r\n    Fields : Field<'a> list\r\n}\r\n\r\ntype FormLayout<'a> = {\r\n    Fieldsets : Fieldset<'a> list\r\n    SubmitText : string\r\n}\r\n\r\nlet renderForm (layout : FormLayout<_>) =\r\n    form [\r\n        for set in layout.Fieldsets ->\r\n            tag \"fieldset\" [] [\r\n                yield tag \"legend\" [] [ encodedText set.Legend ]\r\n\r\n                for field in set.Fields do\r\n                    yield div [ attr \"class\" \"editor-label\" ] [\r\n                        encodedText field.Label\r\n                    ]\r\n                    yield div [ attr \"class\" \"editor-field\" ] [\r\n                        field.Html\r\n                    ]\r\n            ]\r\n        \r\n        yield submitInput layout.SubmitText\r\n    ]\r\n\r\nlet createAlbum (genres:Db.Genre list) (artists:Db.Artist list) = [\r\n    h2 \"Create\"\r\n\r\n    renderForm {\r\n        Fieldsets = \r\n            [\r\n                {\r\n                    Legend = \"Album\"\r\n                    Fields = \r\n                        [\r\n                            {\r\n                                Label = \"Genre\"\r\n                                Html = \r\n                                    select [attr \"name\" \"GenreId\"] [\r\n                                        for g in genres ->\r\n                                            option [ attr \"value\" (g.Genreid.ToString())] [encodedText g.Name]\r\n                                    ]\r\n                            }\r\n                            {\r\n                                Label = \"Artist\"\r\n                                Html = \r\n                                    select [attr \"name\" \"ArtistId\"] [\r\n                                        for a in artists ->\r\n                                            option [ attr \"value\" (a.Artistid.ToString())] [encodedText a.Name]\r\n                                    ]\r\n                            }\r\n                            {\r\n                                Label = \"Title\"\r\n                                Html = \r\n                                    input [ attr \"name\" \"Title\"] \r\n                            }\r\n\r\n                            {\r\n                                Label = \"Price\"\r\n                                Html = \r\n                                    input [ attr \"name\" \"Price\"] \r\n                            }\r\n                            {\r\n                                Label = \"Album Art Url\"\r\n                                Html = \r\n                                    input [ attr \"name\" \"ArtUrl\" ]\r\n                            }\r\n                        ]\r\n                }\r\n            ]\r\n        SubmitText = \"Create\"\r\n    }\r\n\r\n    div [] [\r\n        a [ attr \"href\" Path.Admin.manage] [ encodedText \"Back to list\" ]\r\n    ]\r\n]\r\n```\r\n\r\n```fsharp\r\nlet createAlbum =\r\n    let dbCtx = Db.getContext()\r\n    choose [\r\n        GET >=> warbler (fun _ ->\r\n            let genres = Db.getGenres dbCtx\r\n            let artists = Db.getArtists dbCtx\r\n            html (View.createAlbum genres artists))\r\n        POST >=> \r\n            fun (next : HttpFunc) (ctx : HttpContext) ->\r\n                task {\r\n                    let! album = ctx.BindForm<Album>()\r\n                    Db.createAlbum\r\n                        (\r\n                            int album.ArtistId,\r\n                            int album.GenreId,\r\n                            album.Price,\r\n                            album.Title\r\n                        ) dbCtx \r\n                    return! next ctx\r\n                }\r\n            redirectTo false Path.Admin.manage\r\n    ]\r\n```\r\n"},"comment":{"url":"https://api.github.com/repos/giraffe-fsharp/Giraffe/issues/comments/367209133","html_url":"https://github.com/giraffe-fsharp/Giraffe/issues/126#issuecomment-367209133","issue_url":"https://api.github.com/repos/giraffe-fsharp/Giraffe/issues/126","id":367209133,"user":{"login":"Banashek","id":7094571,"avatar_url":"https://avatars2.githubusercontent.com/u/7094571?v=4","gravatar_id":"","url":"https://api.github.com/users/Banashek","html_url":"https://github.com/Banashek","followers_url":"https://api.github.com/users/Banashek/followers","following_url":"https://api.github.com/users/Banashek/following{/other_user}","gists_url":"https://api.github.com/users/Banashek/gists{/gist_id}","starred_url":"https://api.github.com/users/Banashek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Banashek/subscriptions","organizations_url":"https://api.github.com/users/Banashek/orgs","repos_url":"https://api.github.com/users/Banashek/repos","events_url":"https://api.github.com/users/Banashek/events{/privacy}","received_events_url":"https://api.github.com/users/Banashek/received_events","type":"User","site_admin":false},"created_at":"2018-02-21T04:21:45Z","updated_at":"2018-02-21T04:21:45Z","author_association":"CONTRIBUTOR","body":"I've been using `_name \"PropertyName\"` in my code, but I do see a possible elegant abstraction using quotations.\r\n\r\nYou could get the property names by doing something like:\r\n```\r\nlet m = // Instance of your DTO model\r\nmatch <@@ m @@> with | PropertyGet(_,pi,_) -> pi.Name | _ -> \"\"\r\n```\r\n\r\nObviously that's just a quick fsi result, but you could go based off of that. Not sure how to do it with non-static types, but I'm sure there's a good way to do it if you keep digging."}}
{"actor":{"display_login":"Banashek"}}