Tag Interfaces
Every tag in Partas.Solid is a type, and what a tag can do comes from the interfaces it implements: HtmlContainer takes children, HtmlTag takes the global HTML attributes and the extension methods. Your own components use the same interfaces, usually by inheriting a tag that already has them:
[<Erase>]
type MyButton() =
inherit button() // every button attribute, children, and the extension methods
Foundation interfaces
| Interface | What it gives you |
|---|---|
HtmlElement | The base. Anything that implements it can be a child of another element. |
HtmlTag | Inherits HtmlElement. Adds the global HTML attributes, events and the extension methods. |
HtmlContainer | Inherits HtmlElement. Lets the type take children in a { } block, and adds a children property. |
FlowContainer<'A> | Inherits HtmlElement. Like HtmlContainer, but children must be of type 'A. |
Derived interfaces
| Interface | Inherits | Used for |
|---|---|---|
RegularNode | HtmlTag, HtmlContainer | An element with attributes and children, such as div. |
VoidNode | HtmlTag | An element with attributes and no children, such as input or br. |
FlowNode<'A> | HtmlTag, FlowContainer<'A> | An element with attributes whose children are limited to 'A. |
FragmentNode | HtmlContainer | Renders as a JSX fragment, <></>. Fragment() implements it. |
Switch, for example, is a FlowContainer<IMatch>, so the compiler only accepts Match children inside it.
Child lambda providers
Some components take a function as their child: For passes it each item and its index. The child lambda provider interfaces type that function:
| Interface | The child is |
|---|---|
ChildLambdaProvider<'P1> | 'P1 -> #HtmlElement |
ChildLambdaProvider2<'P1, 'P2> | 'P1 -> 'P2 -> #HtmlElement |
ChildLambdaProvider3<'P1, 'P2, 'P3> | a function of three arguments |
ChildLambdaProvider4<'P1, 'P2, 'P3, 'P4> | a function of four arguments |
ChildLambdaProviderStrict<'P1, 'Children> | 'P1 -> 'Children |
ChildLambdaProviderStrict2 to 4 | the same, with two to four arguments |
The strict variants fix the return type as well as the arguments. For.Keyed implements ChildLambdaProvider2<'T, Accessor<int>>, so its child is fun item index -> .... Repeat<'T> implements ChildLambdaProviderStrict<int, 'T>. Callers pass the function with yield (see lambda children).
Implement one of these interfaces on your own component to take a function child. Inside the component, props.children holds the function. Unbox it to its F# type and call it:
[<Erase>]
type Toggle() =
inherit div()
interface ChildLambdaProvider<Accessor<bool>>
[<SolidTypeComponent>]
member props.View =
let on, setOn = createSignal false
let renderChild: Accessor<bool> -> HtmlElement = unbox props.children
div (style = "display: flex; gap: .5rem; align-items: center") {
button (onClick = fun _ -> setOn (not (on ()))) { "Flip" }
renderChild on
}
export function Toggle(props) {
const PARTAS_OTHERS = omit(props, "children");
const patternInput = createSignal(false);
const on = patternInput[0];
let renderChild;
const clo = props.children;
renderChild = (clo);
return <div style="display: flex; gap: .5rem; align-items: center">
<button onClick={(_arg) => {
patternInput[1](!on());
}}>
Flip
</button>
{renderChild(on)}
</div>;
}
Toggle() {
yield fun on -> span () { if on () then "ON" else "OFF" }
}
export function Cell_c2() {
return <Toggle>
{(on) => <span>
{on() ? ("ON") : ("OFF")}
</span>}
</Toggle>;
}
For a function of two or more arguments, unbox the children to a System.Func, because Solid calls it with all of its arguments at once:
let renderChild: System.Func<Accessor<string>, (string -> unit), HtmlElement> = unbox props.children
renderChild.Invoke(value, setValue)
Polymorph
Polymorph, in Partas.Solid.Polymorphism, inherits HtmlTag. It is for components whose rendered element the caller picks, such as Kobalte's, and adds an .as' extension that takes a tag or a tag value. See Polymorphism.
