我如何与寓言 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 中找到更多信息。
我想创建 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 中找到更多信息。