I've been looking at creating some bindings for the Office Fabric UI React Components
My first attempt using ts2fable hit a dead end - the fabric ui lib has lots of d.ts files that reference each other that make using the tool not ideal - see #740
So then I looked at the way the react toolbox bindings were created and tried that
They make use of the KeyValueList attribute on discriminated unions when defining the various react component prop types
I ended up with something like this
`
type [
| Text of string
let DefaultButton = import
let inline defaultButton b c = React.from DefaultButton b c
But this results in a compiler error
error FABLE: Type List passed as generic param 'P must be decorated w
ith Fable.Core.PojoAttribute or beobj/interface
It seems the Pojo attribute is not applicable to DU?
I could create a record type to represent the button props but that means either setting all the properties each time which is not ideal or creating default record instances again not ideal and not as nice as supplying the list of DU types that worked in earlier versions
Whats the advice on creating definitions for these types of js objects commonly used in config or prop types in react
Here is the IButtonProps documentation - Implementation section defines the props type
thanks in advance
`
Hi @richardjharding! Yes, you're right KeyValueList has been deprecated. Sorry for that, but I think it was necessary because it involved some magic and it confused who tried to use the properties as normal lists. Now, the properties are actual lists and you must convert them explicitly to a JS object using the keyValueList helper. You can still have the same user experience as before by doing the conversion in your helper code, for example:
open Fable.Core.JsInterop
type IButtonProps =
| Text of string
let DefaultButton: ComponentClass =
import "DefaultButton" "office-ui-fabric-react/lib/components/button"
let inline defaultButton (props: IButtonProps list) c =
React.from DefaultButton (keyValueList CaseRules.LowerFirst props) c
If the helper function is inline, Fable will try to optimize the conversion and make it at compile-time.
Do you think it would be worth me starting a page on creating React bindings in the docs, that we can all add to? I got caught out by this too, when starting with Fable and Elmish recently. I was helped on the Fable Gitter, so it feels like people are repeating the help. Maybe we should all answer the questions by banging them in the docs and pointing people to them?
@bentayloruk Sure! Any help with the docs is more than welcome! 馃憪
Yes I think some updated docs on this would be great - its pretty much the first thing you want to do when building anything more complex - happy to help in this
I have a follow up question - the approach works great for my example until I get to fleshing out the props type
How would you deal with nested DUs? eg
type IIconProps=
| IconName of string
type IButtonProps =
| Text of string
| IconProps of IIconProps
This compiles but on the client the nested DU gets converted to an object with data and tag properties
data = "add" <- the value use for IconName
tag = 0
With latest Fable you can use sublists but you need to set | IconProps of IIconProps list (note we're using "IIconProps list") in the parent DU. Check for example this unit test.
@alfonsogarciacaro thanks for that - I've tried that but on the client its not looking correct - so as you suggest my code looks like this
type IIconProps=
| IconName of string
type IButtonProps =
| Text of string
| IconProps of IIconProps list
let DefaultButton: ComponentClass<obj> =
import "DefaultButton" "office-ui-fabric-react/lib/components/button"
let inline defaultButton (props: IButtonProps list) c =
React.from DefaultButton (keyValueList CaseRules.LowerFirst props) c
and in my view I have this
defaultButton [IButtonProps.Text "Foo"
IconProps [IconName "Add"]
] []
this all compiles but my view.fs ends up like this in chrome debugger
export var root = createElement("div", {
className: "content"
}, createElement("h1", {}, "Fabric Demo"), createElement("p", {}, "This pages exists to test the fabric ui bindings."), createElement(DefaultButton, {
text: "Foo",
iconProps: ofArray([new IIconProps(0, "Add")])
}));
and in the react tools my iconprops is not a simple javascript object but some List type?
Think I'm still missing something regards defining or transforming the nested DUs in fable
Can you point me to any other bindings that are up to date with current fable version and authored in this way (or should I use another approach?)
Thanks
Richard
Hmm, that's weird. What version of Fable are you using? (dotnet fable --version) I don't remember exactly but sublists should have been implemented in 1.1.1 or so (latest is 1.1.6).
Maybe not the best example as there's a bit of magic involved, but in the react-native demo an image is created with nested lists. Which are later converted to plain JS objects in Fable.Helpers.ReactNative.
yep that was it - I was on 1.0.8 - think I was on the latest fable elmish template
Once I updated to 1.1.6 it all works great - thanks for your help
Raises a couple of questions - is there a changelog somewhere that tracks these new features?
Is there someway I can signal to a user during use of the bindings that they need a min version of the fable compiler?
Also I'd like to publish these bindings once I'm done - whats another good up to date example I can base them on?
is there a changelog somewhere that tracks these new features?
We want to create GH releases, but at the moment you can check the RELEASE_NOTES. Please note there're three packages (Fable.Core, Fable.Compiler and the dotnet-fable CLI tools). For example you can the release notes for Fable.Core here. Probably these should be added to the Nuget package, I'll check how to do that.
Is there someway I can signal to a user during use of the bindings that they need a min version of the fable compiler?
Fable.Core and the compiler are more or less tied (though we're still refining that part), so just make your library dependent on latest Fable.Core (or the first Fable.Core version that works with your library) and you should be good to go :)
Also I'd like to publish these bindings once I'm done - whats another good up to date example I can base them on?
That'd be very nice! Atm we are making a difference between _pure_ bindings (just metadata no actual code) and helpers. The distinction is important because for the helpers you need to add the source code to the Nuget package. The bindings maintained by fable-compiler org can be found in the fable-import repo. For a library containing both bindings and helper code you can check fable-react-native for example, as it has some of the things you need like nested KeyValue lists (it contains also several hacks, please ask if you don't understand something). But actually the most important part you need is the 2 extra lines to add the source code to the nuget package. In that repo, publishing is not automated, but you can easily do it by running:
dotnet pack
dotnet nuget pusht bin/Debug/Your.Package.1.0.0.nupkg -s nuget.org -k YOUR_NUGET_API_KEY
Hope it helps! 馃槉
Most helpful comment
Hi @richardjharding! Yes, you're right KeyValueList has been deprecated. Sorry for that, but I think it was necessary because it involved some magic and it confused who tried to use the properties as normal lists. Now, the properties are actual lists and you must convert them explicitly to a JS object using the
keyValueListhelper. You can still have the same user experience as before by doing the conversion in your helper code, for example: