Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access Fable's promise results

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
like image 659
Fred Avatar asked Dec 31 '22 10:12

Fred


1 Answers

If you do it following the elmish pattern, you could do the following. This might give some errors but should be enough to get you going:

Use mkProgram because it gives you flexibility to specify Cmd callbacks

Program.mkProgram init update view
    |> Program.withReact "elmish-app"
    |> Program.withConsoleTrace
    |> Program.run

Make sure you have a distinction between your model and message

type Model = {
    UserMessage: string
    ErrorMessage: string
}

Add a message type to receive the promise result and to handle an error:

type Message = 
        | Loading
        | ShowResponse of Result<string,string>
        | Error of exn

Your init should return model and Cmd now as the signature has changed (see update below):

let init() = { UserMessage= "" ; ErrorMessage= "" } , Cmd.ofMsg Loading

Change your update function, the signature has now changed as we are using mkProgram. Note that when you call Cmd.ofPromise you are passing the ShowResponse and Error messages as callbacks -ShowResponse on success and Error if anything bad happens in your promise code.

//update signature has changed from
msg -> model -> model
//to:
msg -> model -> (model*Cmd<msg>)

let update msg model = 
            match msg with 
            | Loading -> 
                { model with UserMessage= "Loading" }, 

                    Cmd.ofPromise (getMe ) () ShowResponse Error
            | ShowResponse  resp -> 
                match resp with
                    | Ok str -> 
                        { model with UserMessage = str }, Cmd.none
                    | Error str -> 
                        { model with ErrorMessage = str }, Cmd.none                     
            | Error exn ->  { model with ErrorMessage = string exn }, Cmd.none
like image 119
onemorecupofcoffee Avatar answered Jan 02 '23 23:01

onemorecupofcoffee