Just starting out with Fable / Elmish / F#, but getting stuck getting a basic concept done...
The goal of the code is to execute a simple HTTP GET request and, thereafter, to post the results of the GET request to the user on a web page.
The idea was to use Fable Promise
or async
to execute the GET request and return the result, thereafter the result can be displayed on a web page using Elmish and Fable.
I expected that the web page (localhost) will output a text string containing the response of of the GET request, instead I only got "[object Promise]" on the web page. The desired response of the GET request is however logged on the Browser console. If I issue a printfn command with the txt
in the body of the promise (before the return
) I can also see the result of the GET request in the browser console.
Thus: I have no idea how to access the results of the promise. Please help, what am I missing? The sample apps from [Fable][1] , there is a image "container" created and an image placed based on the results of the HTTP get, but I need to work with text (json string).
Here is my code:
module App
open Elmish
open Fable.React
open Elmish.React
open Fable.React.Props
open Fable.Import
open Fable.PowerPack
open Fetch
// CallAPI
let getMe = promise {
let! res = fetch "https://reqres.in/api/users/2" []
let! txt = res.text()
// Access your resource here
Browser.console.log txt
return txt
}
//State
type MyInputString =
{Response : string}
// Message
type Message =
|ShowResponse
|Error
let getFromAPI =
getMe |> sprintf "%A"
//Update
let update msg model =
match msg with
|ShowResponse -> {Response = getFromAPI }
|Error -> {Response = "Errrrrr"}
//Helper function
let text(content:string) : ReactElement = unbox content
//Helper function
let init() = {Response = ""}
//View
let view model dispatch =
div
[]
[
div [] [ input[ Type "text"] ]
button [ OnClick (fun _ -> dispatch ShowResponse ) ] [ text "Show Me"]
div [] []
div [] [text (model.Response)]
]
Program.mkSimple init update view
|> Program.withReactSynchronous "werk"
|> Program.withConsoleTrace
|> Program.run ```
[1]: https://fable.io
Promises are a fundamentally imperative construct--you can only get the value out of them using side effects. They aren't like a Task in C# which can also yield a value synchronously. That means your model MUST have two states related to getMe: the state it's in while waiting for getMe to finish, and the state it's in when getMe completes. Each state will have its own message and its own case in update. The handler for the first case will begin getMe using Cmd.ofSub, and will ensure that when the promise completes, it has the side effect of sending the second message, which will show the result of the promise in the UI.
So in addition to ShowResponse and Error, you need a third message MakeRequest. Code below is just a sketch (not tested) but illustrates the point:
// Message
type Message =
| MakeRequest
| ShowResponse of string
| Error
let getMe dispatch =
promise {
let! res = fetch "https://reqres.in/api/users/2" []
let! txt = res.text()
// Access your resource here
Browser.console.log txt
dispatch (ShowResponse txt)
}
//Update
let update msg model =
match msg with
| MakeRequest -> { model with Response = "Wait..." },
Cmd.ofSub(fun dispatch -> getMe dispatch |> Promise.start)
| ShowResponse msg -> { model with Response = msg }, Cmd.empty
| Error -> { model with Response = "Errrrrr"}, Cmd.empty