我如何与寓言 F# 中的 Javascript 代码互操作?

How do I interop with this Javascript code, from Fable F#?

我想创建 Plotly.js 库到 Fable 的绑定。 我在看 this js code

import React from 'react';
import Plot from 'react-plotly.js';
class App extends React.Component {
  render() {
    return (
      <Plot
        data={[
          {
            x: [1, 2, 3],
            y: [2, 6, 3],
            type: 'scatter',
            mode: 'lines+points',
            marker: {color: 'red'},
          },
          {type: 'bar', x: [1, 2, 3], y: [2, 5, 3]},
        ]}
        layout={ {width: 320, height: 240, title: 'A Fancy Plot'} }
      />
    );
  }
}

我创建简单测试绑定的(错误)尝试看起来像这样

open Fable.Core
open Fable.Core.JsInterop
open Browser.Types
open Fable.React

// module Props =
type Chart =
    |X of int list
    |Y of int List
    |Type of string

type IProp =
    | Data of obj list

let inline plot (props: IProp) : ReactElement =
    ofImport "Plot" "react-plotly.js" props []

let myTrace = createObj [
    "x" ==> [1,2,3]
    "y" ==> [2,6,3]
    "type" ==> "scatter"
    "mode" ==> "lines"
]

let myData = Data [myTrace]

let testPlot = plot myData

但是显然不行。我如何让它工作?另外,{[...]} 是什么意思?我是 Javascript 的新手,据我所知 {...} 表示一个必须包含名称值对的对象,而 [...] 表示一个数组。所以 {[...]} 似乎表示一个具有单个无名成员的对象,它是一个数组,但据我所知,没有具有无名成员的对象。

我已经能够重现您链接的示例。请注意,我没有 Plotly,我采用了经验主义的方式,所以事情可能会有所改进 :)

我已经创建了代码,如果我必须在我的生产应用程序中使用它,我可能会这样做。所以代码比你的问题多一点,因为我不使用 createObj

如果您不喜欢类型化的 DSL,您可以随时简化它,删除它并使用 createObj 或匿名记录,就像我对标记 属性 所做的那样:)

您需要在项目中同时安装 react-plotly.js plotly.js

open Fable.Core.JsInterop
open Fable.Core
open Fable.React

// Define props using DUs this helps create a typed version of the React props
// You can then transform a list of props into an object using `keyValueList`

[<RequireQualifiedAccess>]
type LayoutProps =
    | Title of string
    | Width of int
    | Height of int

// GraphType is marked as a `StringEnum` this means
// the value will be replace at compile time with
// their string representation so:
// `Scatter` becomes `"scatter"`
// You can customise the output by using `[<CompiledName("MyCustomName")>] 

[<RequireQualifiedAccess; StringEnum>]
type GraphType =
    | Scatter
    | Bar

[<RequireQualifiedAccess; StringEnum>]
type GraphMode =
    | Lines
    | Points
    | Markers
    | Text
    | None

[<RequireQualifiedAccess>]
type DataProps =
    | X of obj array
    | Y of obj array
    | Type of GraphType
    | Marker of obj

    // This is an helpers to generate the `flagList` waited by Plotly, if you don't like it you can just remove 
    // member and replace it with `| Mode of string` and so you have to pass the string by yourself
    static member Mode (modes : GraphMode seq) : DataProps =
        let flags =
            modes
            |> Seq.map unbox<string> // This is safe to do that because GraphMode is a StringEnum
            |> String.concat "+"

        unbox ("mode", flags)


[<RequireQualifiedAccess>]
type PlotProps =
    | Nothing // Should have real props here is there exist more than Data and Layout

    // Here notes that we are asking for an `Array` or Data
    // Array being the type expected by the JavaScript library
    // `DataProps seq` is our way to represents props
    static member Data (dataList : (DataProps seq) array) : PlotProps =
        let datas =
            dataList
            |> Array.map (fun v ->
                keyValueList CaseRules.LowerFirst v // Transform the list of props into a JavaScript object
            )

        unbox ("data", datas)

    static member Layout (props : LayoutProps seq) : PlotProps =
        unbox ("layout", keyValueList CaseRules.LowerFirst props)

// All the example I saw from react-plotly was using this factory function to transform the plotly library into a React component
// Even, the example you shown if you look at the Babel tab in the live example
let createPlotlyComponent (plotly : obj) = import "default" "react-plotly.js/factory"

// Immport the plotly.js library
let plotlyLib : obj = import "default" "plotly.js"

// Apply the factory on the plotly library
let Plot : obj = createPlotlyComponent plotlyLib

// Helper function to instantiate the react components
// This is really low level, in general we use `ofImport` like you did but if I use `ofImport` then I got a React error
let inline renderPlot (plot : obj) (props : PlotProps list) =
    ReactBindings.React.createElement(plot, (keyValueList CaseRules.LowerFirst props), [])

let root =
    // Here we can render the plot using our Typed DSL
    renderPlot
        Plot
        [
            PlotProps.Data
                [|
                    [
                        DataProps.X [| 1; 2; 3 |]
                        DataProps.Y [| 2; 6; 3 |]
                        DataProps.Type GraphType.Scatter
                        DataProps.Mode
                            [
                                GraphMode.Lines
                                GraphMode.Points
                            ]
                        DataProps.Marker {| color = "red" |}
                    ]
                    [
                        DataProps.Type GraphType.Bar
                        DataProps.X [| 1; 2; 3 |]
                        DataProps.Y [| 2; 5; 3 |]
                    ]
                |]
            PlotProps.Layout
                [
                    LayoutProps.Width 640
                    LayoutProps.Height 480
                    LayoutProps.Title "A Fancy Plot"
                ]
        ]

我来晚了一点,但如果您仍然希望将 plotly.js 与 Fable 一起使用,我想给您一个不同的选择。

在过去一个月左右的时间里,我一直在为 plotly.js 开发绑定,目前它处于非常可用的状态。话虽这么说,我不会说它已经准备好生产了。

这就是您要转换的示例的样子 Feliz.Plotly:

open Feliz
open Feliz.Plotly

let chart () =
    Plotly.plot [
        plot.traces [
            traces.scatter [
                scatter.x [ 1; 2; 3 ]
                scatter.y [ 2; 6; 3 ]
                scatter.mode [
                    scatter.mode.lines
                    scatter.mode.markers
                ]
                scatter.marker [
                    marker.color color.red
                ]
            ]
            traces.bar [
                bar.x [ 1; 2; 3 ]
                bar.y [ 2; 5; 3 ]
            ]
        ]
        plot.layout [
            layout.width 320
            layout.height 240
            layout.title [
                title.text "A Fancy Plot"
            ]
        ]
    ]

您可以在 here 中找到更多信息。