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

时间:2019-06-23 09:18:34

标签: 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

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

2 个答案:

答案 0 :(得分:2)

我已经能够复制您链接的示例。请注意,我不擅长情节,我采用了经验主义的方式,所以事情可能会有所改善:)

我已经创建了代码,就像我不得不在生产应用程序中使用它那样。因此,比我的问题要多的代码是因为我不使用createObj

如果您不喜欢键入的DSL,则可以随时对其进行简化,将其删除并像我对marker属性那样使用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"
                ]
        ]

答案 1 :(得分:1)

我在这里参加聚会有点晚了,但是如果您仍然希望将Fablely.js与寓言结合使用,想给您一个不同的选择。

在过去的一个月左右的时间里,我一直在为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中找到更多信息。