Dynamic Chart - Fable

701 views Asked by At

I have a project with model update view architecture using fable-elmish. And I have to download files every minute and read those files. How can I download in the update function and how can I read and parsing to Json?

I need to create dynamic charts using Fable too. Someone knows how?

I have part of my code here:

let update (msg : Msg) (model : Model) =
  match msg with
  | GetData -> 
    model, 
    Cmd.ofPromise 
      (fun () -> 
        promise {
          let wc = new WebClient()
          wc.DownloadData("https://www.quandl.com/api/v1/datasets/LBMA/SILVER.json", "SILVER.json")
          wc.DownloadData("https://www.quandl.com/api/v1/datasets/LBMA/GOLD.json", "GOLD.json")
          // Read 2 files
          // Return 2 Json.Object
        })
      ()
      (fun silver gold -> GotData silver gold)
      (fun e -> GotError e.Message)
  | GotData silver gold -> 
    (Model.SilverData silver, Model.GoldData gold), // I think this doesn't work
    Cmd.ofPromise 
      (fun () -> Promise.sleep 60000)
      ()
      (fun () -> GetData)
      (fun e -> GetData)
1

There are 1 answers

0
WalternativE On

If you have a periodic event which should cause some action in your Elmish application I would use a subscription. The following code snippet shows a function which sets an interval that causes a command dispatch every 10 minutes.

let timer initial =
    let sub dispatch =
        window.setInterval(fun _ -> dispatch LoadDataSet; console.log("Timer triggered")
            , 1000 * 60 * 10) |> ignore

    Cmd.ofSub sub

You would use the Program.withSubscription function to add the subscription to your main dispatch loop.

I would use the Fable PowerPack package for its fetch and promise support to get the datasets. The following code would fetch the documents from your specified endpoints, parse them as values of the DataSet type and return them as a value of the SilverAndGold model type on the successful path of the promise.

type DataSet =
    { column_names : string list
      data : (string * float * float * float) list }

type SilverAndGold =
    { Silver : DataSet
      Gold : DataSet }

...

let fetchDataSets () = promise {
    let! silverData = Fetch.fetchAs<DataSet> "https://www.quandl.com/api/v1/datasets/LBMA/SILVER.json" []
    let! goldData = Fetch.fetchAs<DataSet> "https://www.quandl.com/api/v1/datasets/LBMA/GOLD.json" []

    return { Silver = silverData; Gold = goldData }
}

In the update function of the Elmish app you can see how the promise execution is triggered. On every LoadDataSet message dispatched by our subscription we create a command of the promise which either results in a DataSetLoaded message containing the datasets or in an Error.

let update (msg:Msg) (model:Model) =
    match msg with
    | LoadDataSet ->
        model, Cmd.ofPromise fetchDataSets () DataSetLoaded Error
    | DataSetLoaded silverGold ->
        // here you could process you silver and gold datasets
        console.log silverGold
        Some silverGold, Cmd.none
    | Error e -> model, Cmd.none

We can use the Fable bindings for the Recharts library to plot our datasets. The following code shows how we transform and trim the datasets (rendering all datapoints would be quite taxing in the browser) and display them as line charts in the view function.

type ChartDataPoint =
    { Date : string
      Usd : float
      Gbp : float
      Euro : float }

let toChartData (dataSet : DataSet) =
    dataSet.data
    |> List.map (fun (dt, usd, gbp, eur) ->
        { Date = dt; Usd = usd; Gbp = gbp; Euro = eur } )
    |> Array.ofList
    |> Array.take 1000
    |> Array.rev

let priceChart (chartData : ChartDataPoint[]) =
    lineChart
        [ Chart.Data chartData
          Chart.Width 600.
          Chart.Height 500. ] [
        xaxis [ Cartesian.DataKey "Date" ] []
        yaxis [] []
        tooltip [] []
        legend [] []
        line [ Cartesian.Type "monotone"; Cartesian.DataKey "Gbp" ] []
        line [ Cartesian.Type "monotone"; Cartesian.DataKey "Euro" ] []
        line [ Cartesian.Type "monotone"; Cartesian.DataKey "Usd" ] []
    ]

let view (model : SilverAndGold option ) dispatch =
  div [ ] [
        match model with
        | Some sets ->
            yield h2 [] [ str "Silver" ]
            yield priceChart (toChartData sets.Silver)
            yield h2 [] [ str "Gold" ]
            yield priceChart (toChartData sets.Gold)
        | None ->
            yield h2 [] [ str "No data :("]
    ]

I cooked up a very little Elmish app which includes all these topics. You can find it here here and adapt it according to your needs.