Oxpecker DSL
Partas.Solid is a fork of Oxpecker.Solid and keeps its DSL. Attributes go in a constructor call, children go in a computation expression after it:
tag (attribute = value, ...) {
child
child
}
Elements
Each HTML element is a class, and its attributes are optional constructor parameters. Attribute names that are F# keywords get an apostrophe: class', type', for', when'. An attribute with a fixed set of values takes a case of a union in Attr, and the JSX gets its string:
button (class' = "btn", type' = Attr.ButtonType.Button, title = "A plain button") { "Button" }
export function Cell_c1() {
return <button class="btn"
type="button"
title="A plain button">
Button
</button>;
}
Event handlers receive the event type of their event. onClick takes a Dom.MouseEvent -> unit, onInput a Dom.InputEvent -> unit; Dom is in Fable.Core.TS.
Children
The computation expression after an element holds its children: strings, numbers and other elements, in any mix.
div () {
"Button label"
button () { "Button" }
span () { "Count:" }
5
}
export function Cell_c2() {
return <div>
Button label
<button>
Button
</button>
<span>
Count:
</span>
{5}
</div>;
}
An element with no children does not need the braces: input (type' = "checkbox"). Void elements such as input, br and img do not accept any.
Ordinary F# works around and inside the block. An if becomes a ternary in the JSX:
let name = "Solid"
div () {
p () { $"Hello, {name}!" }
if name.Length > 3 then
p () { "That is a long name." }
}
export function Cell_c3() {
return <div>
<p>
{concat("Hello, ", "Solid", "!")}
</p>
{("Solid".length > 3) ? (<p>
That is a long name.
</p>) : (undefined)}
</div>;
}
Embedded JSX
JSX.Element collides with HtmlElement in the F# compiler, so the builder does not take Fable's JSX.jsx directly. unbox it. JSX.jsx only takes a string literal:
div () {
unbox<HtmlElement> (JSX.jsx "<button>Raw JSX button</button>")
}
export function Cell_c4() {
return <div>
<button>Raw JSX button</button>
</div>;
}
Lambda children
Some components, such as For, take a function as their child instead of elements.
ul () {
For.Keyed(each = [| "one"; "two"; "three" |]) {
yield fun item _ -> li () { $"Item {item}" }
}
}
export function Cell_c5() {
return <ul>
<KeyedFor each={["one", "two", "three"]}>
{(item, _arg) => <li>
{concat("Item ", item)}
</li>}
</KeyedFor>
</ul>;
}
To write a component that takes a function child, see tag interfaces.
Extension methods
Spreads, custom and data-* attributes, style objects and refs are chained after the constructor: div(class' = "card").data("id", "42"). See extension methods.
