### Run the Blazor Application
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/90 Tooling/10 Code Generation/README.md
Starts the Fun.Blazor WebAssembly application. This command should be run after all setup and code generation steps are completed.
```bash
dotnet run
```
--------------------------------
### Install and List Fun.Blazor Templates
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/20 Get Started/README.md
Use these dotnet CLI commands to install the Fun.Blazor templates and list available templates for your project.
```shell
dotnet new --install Fun.Blazor.Templates::4.2.0
```
```shell
dotnet new list --tag Fun.Blazor
```
```shell
dotnet new fun-wasm -o FunBlazorDemo1
```
--------------------------------
### Install Fun.Blazor Templates and CLI
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/90 Tooling/10 Code Generation/README.md
Installs the Fun.Blazor project templates and the CLI tool. This is part of the setup for a new Fun.Blazor project.
```bash
dotnet new install Fun.Blazor.Templates
dotnet tool install -g Fun.Blazor.Cli --version 4.1.1
```
--------------------------------
### Install Fun.Blazor.Cli
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/90 Tooling/10 Code Generation/README.md
Installs the Fun.Blazor command-line interface tool globally. Ensure you have the specified version.
```bash
dotnet tool install -g Fun.Blazor.Cli --version 4.1.1
```
--------------------------------
### Inline Style Example
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/12 Style/README.md
Demonstrates how to apply a simple inline style to a div element.
```fsharp
div {
style {
color "red"
}
}
```
```html
```
--------------------------------
### Scoped and Global Store Example
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/20 Get Started/README.md
Demonstrates how to define scoped stores for session-specific data (like theme) and global singleton stores for shared data across all users in Blazor Server mode.
```fsharp
type IShareStore with // scoped for the session in blazor server mode
member store.IsDark = store.CreateCVal("IsDark", true)
type IGlobalStore with // Singleton store, shared for all. Used in server-side blazor to share some data for all connected users.
...
```
--------------------------------
### F# Blazor Inject Style Component
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/10 About/README.md
A Blazor component example demonstrating the use of html.inject for dependency injection in F#.
```fsharp
{{BlazorStyleComp}}
```
--------------------------------
### Blazor App Component with MudBlazor and Adaptive
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/90 Tooling/10 Code Generation/README.md
An example of an F# Blazor application component using MudBlazor components and the adaptive data binding library. It includes a counter example with a button.
```fsharp
[]
module FunWasm.App
open FSharp.Data.Adaptive
open MudBlazor
open Fun.Blazor
let app = fragment {
MudThemeProvider
adapt {
let amount = 1
let! count, setCount = cval(1).WithSetter()
div {
div { $"Here is the count {count}" }
MudButton {
Color Color.Primary
Variant Variant.Filled
OnClick(fun _ -> setCount (count + amount))
"Increase by "
amount
}
}
}
}
```
--------------------------------
### Create a Fun.Blazor Component for C# Blazor Projects
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/95 FAQ/README.md
Demonstrates how to define a simple counter component using Fun.Blazor's DSL, which can be used within existing C# Blazor projects. Ensure you have the necessary Fun.Blazor setup.
```fsharp
type Counter() as this =
inherit FunComponent()
[]
member val Count = 0 with get, set
override _.Render() =
div {
this.Count
button {
onclick (fun _ -> this.Count <- this.Count + 1)
"Increase"
}
}
```
--------------------------------
### Region Isolation with AdaptiveView Example
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/15 Region/README.md
Shows how 'adapt' (which uses 'region' internally) isolates content, ensuring that dynamic rendering within it does not amplify sequence number changes.
```fsharp
div {
// sequence 0
adapt { // sequence 1
match! isLoading with
| true -> loader
| false -> someDataView
}
div {
// sequence 2
"hi" // sequence 3
}
}
```
--------------------------------
### Route Definition for Application
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/20 Get Started/README.md
Defines application routes using Fun.Blazor's routing mechanism, mapping specific paths to components. This example shows how to set up routes for 'page1' and a default route for 'Comp2'.
```fsharp
let private routes =
html.route [
routeCi "/page1" (Comp1.Create())
routeAny (Comp2.Create())
]
let app =
div {
header
routes
footer
}
```
--------------------------------
### Component Composition with Fragments
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/20 Get Started/README.md
Shows how to compose components using smaller, reusable fragments. This approach enhances optimization, hot-reloading, and IntelliSense performance. It also includes an example of using Elmish for state management within a fragment.
```fsharp
// Make your fragment smaller, so you can compose it in a cleaner way and get better inline optimization, hot-reload speeding, and intellisense performance
let private fragment1 = div {...}
let private fragment2 (shareStore: IShareStore) =
adaptiview {
let! isDark, setIsDark = shareStore.IsDark.WithSetter()
div { ... }
}
// Or use Elmish if you like
let private fragment3 = html.elmish (init, update, view)
type Comp1 =
// So we can provide overloads for future iteration
static member Create() =
html.inject (fun (svc1, shareStore: IShareStore, ...) ->
div {
fragment1
fragment2 shareStore
fragment3
}
)
```
--------------------------------
### Dynamic Rendering with Adaptive Values
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/14 Classes and Functions/README.md
Example of using 'cval' (current value) and 'adapt' to create dynamic content within functions. The 'adapt' block re-renders when 'currentName' changes, while other parts of the component do not.
```fsharp
let staticChild name (currentName: string cval) =
button {
// trigger currentName changes
onclick(fun _ -> currentname.Publish name)
$"Click {name}!"
}
let staticParent () =
let currentName = cval("Nobody")
fragment {
// doesn't re-render
h1 { $"Initial Name: {currentName.Value}" }
// render on currentName changes
adapt {
let! currentName = currentName
h1 { $"Hello, {currentName}!" }
}
staticChild "Peter" currentName
staticChild "Frank" currentName
}
```
--------------------------------
### Functional Style Routing Example
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/16 Routing/README.md
This snippet demonstrates the functional style of routing in Fun.Blazor using a helper function like 'routeCi'. It's useful for defining routes that return view fragments or other components.
```fsharp
let routeCi (path: string) (view: unit -> ViewFragment) =
let routeData = {
Path = path
Data = None
}
fun (url: UrlString) ->
if url.Path = path then
Some (view ())
else
None
let demoView () =
div {
"This is the demo view!"
}
let routes =
[ routeCi "/demo" demoView ]
let router = Router'' {
AppAssembly(Assembly.GetExecutingAssembly())
Routes(routes)
NotFound(fun _ -> div {"Page not found"})
}
```
--------------------------------
### F# Computation Expression Style Example
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/90 Tooling/10 Code Generation/README.md
Demonstrates the generated computation expression (CE) style for Blazor components, using MudBlazor components. Requires LangVersion set to preview for CustomOperation override.
```fsharp
open Fun.Blazor
open MudBlazor
let alertDemo =
MudCard' [
MudAlert'() {
Icon Icons.Filled.AccessAlarm
childContent "This is the way"
}
]
```
--------------------------------
### Configure Assembly Trimming Behavior
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/21 Publish Trim/README.md
Manually control the trimming behavior for specific assemblies by adding configuration to your root project file. This example configures FSharp.Data and FSharp.Control.Reactive to be linked and trimmed. Be cautious, as incorrect configuration may cause issues.
```xml
linktruelinktrue
```
--------------------------------
### Dynamic Sequence Number Example
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/15 Region/README.md
Illustrates how dynamic content, like conditional rendering, can cause sequence numbers to change in Fun.Blazor.
```fsharp
div {
// sequence 0
if isLoading then
loader
div {
// sequence may change
"hi"
}
}
```
--------------------------------
### Register Custom Elements for Fun.Blazor
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/82 Custom Elements/README.md
Register custom elements for Fun.Blazor within the ASP.NET Core services configuration. Ensure the `Fun.Blazor.CustomElements` NuGet package is installed.
```fsharp
services.AddServerSideBlazor(fun options -> options.RootComponents.RegisterCustomElementForFunBlazor(Assembly.GetExecutingAssembly()))
```
--------------------------------
### Blazor Interactive Server Counter Page
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/16 Routing/README.md
An example of a Blazor page component using the interactive server render mode. It includes a simple counter with a button to increment it and displays the current count and page title.
```fsharp
[
[]
type Counter() =
inherit FunComponent()
let mutable count = 0
override _.Render() = fragment {
PageTitle'' { "Counter" }
p { $("Current count: {count}") }
button {
onclick (fun _ -> count <- count + 1)
"Click me"
}
}
```
--------------------------------
### Adaptive Rendering - Prefer
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/13 Interactive Nodes/README.md
Demonstrates the preferred way to structure adaptive rendering by keeping static content outside the `adapt` block and only wrapping dynamic content. This optimizes performance by minimizing re-renders.
```fsharp
// ✅ Prefer
fragment {
$"Hello static string" // Stays static
div { "Hello static node" } // Stays static
p { // Stays static
div { // Stays static
adapt {
let! content = myAValue
fragment { $"Hello dynamic content {content}" } // will re-render
}
}
}
}
```
--------------------------------
### Write Demo Components in F#
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/30 How to Contribute/README.md
Use this structure to create new demo components. Ensure the namespace and entry point function name are exact.
```fsharp
// The namespace should keep exactly the same
module Fun.Blazor.Docs.Wasm.Demos.DemoName
open Fun.Blazor
// must be called entry without arguments
let entry = div { "hi" }
```
--------------------------------
### Scoped Service Creation with html.scope
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/42 Dependency Injection/README.md
Shows how to use `html.scope` to create a new DI scope, allowing for isolated data or service state management within a specific part of the component tree.
```fsharp
builder.Root.Add(html.scope(fun (scopedService) ->
div {
p {
text ($"Scoped Service Value: {scopedService.Value}")
}
}
))
```
--------------------------------
### Adaptive Data Node with Input
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/13 Interactive Nodes/README.md
Demonstrates creating an adaptive node that displays and updates an age value based on user input. It uses `adaptiview` to track changes and re-render efficiently.
```fsharp
module DynamicViews =
open FSharp.Data.Adaptive
open Fun.Blazor
let adaptiveNode =
adapt {
let! age, setAge = cval(10).WithSetter()
section {
$"Age: {age}"
br
input {
type' "number"
value age
onchange (fun event -> unbox event.Value |> int |> setAge)
}
}
}
```
--------------------------------
### Using Custom Style Operations
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/12 Style/README.md
Demonstrates the usage of the custom VStack, HStack, and EvenHStack operations within style blocks.
```fsharp
let MyItems = ul {
// display: flex; flex-direction: column;
style { VStack }
// content
}
let MyItems2 = ul {
// display: flex;
style { HStack }
// content
}
let MyItems3 = ul {
// display: flex; justify-content: space-evenly;
style { EvenHStack }
// content
}
```
--------------------------------
### Create New Fun.Blazor Wasm Project
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/90 Tooling/10 Code Generation/README.md
Creates a new Fun.Blazor WebAssembly project with the specified output directory. Navigates into the new project directory.
```bash
dotnet new fun-wasm -o FunWasm
cd FunWasm
```
--------------------------------
### Basic Service Injection with html.inject
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/42 Dependency Injection/README.md
Demonstrates how to inject a service into a component using `html.inject`. The service type is specified as a generic parameter, and the component's DOM is built using a provided render fragment.
```fsharp
builder.Root.Add(html.inject(fun (service) ->
div {
p {
text ($"Hello from MyService: {service.Message}")
}
}
))
```
--------------------------------
### Add Fun.Blazor Component Bindings
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/17 Bindings/README.md
Use these commands to add specific Fun.Blazor component library bindings to your project. Ensure the version matches your requirements.
```bash
dotnet add package Fun.Blazor.AntDesign --version 1.6.1
```
```bash
dotnet add package Fun.Blazor.ApexCharts --version 6.1.0
```
```bash
dotnet add package Fun.Blazor.BlazorMonaco --version 3.4.0
```
```bash
dotnet add package Fun.Blazor.Diagrams --version 3.0.4.1
```
```bash
dotnet add package Fun.Blazor.Microsoft.Authorization --version 10.0.8
```
```bash
dotnet add package Fun.Blazor.Microsoft.FluentUI --version 4.14.2
```
```bash
dotnet add package Fun.Blazor.Microsoft.QuickGrid --version 10.0.8
```
```bash
dotnet add package Fun.Blazor.Microsoft.Web --version 10.0.8
```
```bash
dotnet add package Fun.Blazor.MudBlazor --version 9.4.0
```
```bash
dotnet add package Fun.Blazor.Radzen --version 10.4.3
```
--------------------------------
### Run Fun.Blazor Code Generation
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/90 Tooling/10 Code Generation/README.md
Executes the Fun.Blazor code generation command. By default, it generates code in the 'Fun.Blazor.Bindings' folder. Use the -o flag to customize the output directory.
```bash
fun-blazor generate ./YourApplication.fsproj
```
--------------------------------
### Configure MudBlazor Services
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/90 Tooling/10 Code Generation/README.md
Adds MudBlazor services to the application's service collection in Startup.fs. This is a necessary step for using MudBlazor components.
```text
+ open MudBlazor.Services
+ builder.Services.AddMudServices()
```
--------------------------------
### Defining CSS Rulesets
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/12 Style/README.md
Illustrates how to define reusable CSS rulesets for classes like '.page', '.title', and '.text'.
```fsharp
module SharedClasses =
let Page = ruleset ".page" {
margin 0
padding "1em"
displayFlex
flexDirectionColumn
height "100vh"
}
let Title = ruleset ".title" { fontSize "calc(10px + 3vmin)" }
let Text = ruleset ".text" { fontSize "calc(10px + 1vmin)" }
```
--------------------------------
### F# Blazor Attribute Placement for Ref
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/10 About/README.md
Demonstrates the correct placement of special attributes like 'ref' within a Blazor computation expression to ensure proper handling by Blazor.
```fsharp
div {
attributes ...
ref (fun x -> ()) // ✅
childContent [| ... |]
}
```
```fsharp
div {
attributes ...
ref (fun x -> ()) // ✅
div { }
}
```
--------------------------------
### Serve a Fun.Blazor Page with Htmx Integration
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/80 Htmx/README.md
Configure the application to map Fun.Blazor routes, including essential scripts like lazyBlazorJs. Demonstrates using custom elements for prerendering and Htmx for dynamic content fetching and component injection.
```fsharp
app.MapFunBlazor(fun _ -> html' {
head {
...
CustomElement.lazyBlazorJs ()
}
body {
// We can use html.customElement to prerender something and connect to server with websocket at some point
html.customElement (
ComponentAttrBuilder().Add((fun x -> x.post_id), post.Id),
renderAfter = RenderAfter.InViewport
)
// For more complex cases, we can use htmx to fetch customElement and then connect to server with websocket
div {
hxTrigger hxEvt.intersect
hxGetCustomElement (QueryBuilder().Add((fun x -> x.post_id), post.Id))
hxSwap_outerHTML
}
// We can also just request any blazor component as a static dom content and inject at some point based on htmx
div {
hxTrigger hxEvt.intersect
hxGetComponent (QueryBuilder().Add((fun x -> x.PostId), post.Id))
hxSwap_outerHTML
}
// Or prerender directly and later on the PostComment itself will have htmx integration
html.blazor (ComponentAttrBuilder().Add((fun x -> x.PostId), post.Id))
// script for htmx
script { src "https://unpkg.com/htmx.org@1.9.9" }
}
})
```
--------------------------------
### Watching a Store with Fun.Blazor
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/13 Interactive Nodes/README.md
Illustrates how to use `html.watch` with an `IStore` to display its current value. Stores provide a simpler API for state management compared to observables and can be bound to dynamic attributes.
```fsharp
module ReactiveViews =
open Fun.Blazor
let store: IStore = new Store(initialAge)
let view() =
let storeNode =
html.watch(store, (fun num -> fragment { $"Store: {num}" }))
article {
p { storeNode }
}
```
--------------------------------
### Watching an Observable with Fun.Blazor
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/13 Interactive Nodes/README.md
Shows how to hook up an `IObservable` to Fun.Blazor using the `html.watch` API. This allows the UI to react to emissions from the observable, such as a timer.
```fsharp
module ReactiveViews =
open System
open Fun.Blazor
open FSharp.Control.Reactive
let obs = Observable.interval (TimeSpan.FromSeconds(1.))
article {
h1 { "My View" }
p {
html.watch(obs, (fun num -> fragment { $"Number: {num}" }), 0)
}
}
```
--------------------------------
### Prefer: Efficient Rendering with html.watch
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/13 Interactive Nodes/README.md
This pattern is preferred for efficiency. It keeps static content static and only re-renders the specific part of the node that depends on the observable value.
```fsharp
// ✅ Prefer
fragment {
$"Hello static string" // will stay static
div { "Hello static node" } // will stay static
p { // will stay static
div { // will stay static
"Hello dynamic content " // will stay static
html.watch(
obs,
(fun value -> fragment { $"{value}" }), //will re-render
0
)
}
}
}
```
--------------------------------
### Generate Fun.Blazor Bindings
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/90 Tooling/10 Code Generation/README.md
Runs the Fun.Blazor code generation command within the project directory. This command should be executed after modifying the .fsproj file.
```bash
dotnet fun-blazor generate
```
--------------------------------
### Applying Style Sheets to an Article
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/12 Style/README.md
Demonstrates how to compose an article element with various classes and style tags, including shared rulesets.
```fsharp
let HomePage = article {
class' "page home"
h1 { class' "title"; "This is the title" }
section {
p { class' "text"; "This is some text" }
p { "This is another text" }
}
styleElt {
ruleset ".home" {
fontSize "medium"
boxShadow "5px 5px 0.5em rgba(0, 0, 0, 0.5)"
}
SharedClasses.Page
SharedClasses.Title
SharedClasses.Text
}
}
```
```html
This is the title
This is some text
This is another text
```
--------------------------------
### Sharing Inline Styles
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/12 Style/README.md
Shows how to define and apply shared inline styles using a module and a CSS builder.
```fsharp
module SharedStyles =
let ClickableGreen = css {
cursorPointer
color "green"
}
let styledDiv = div {
style {
SharedStyles.ClickableGreen
fontSize 16
}
}
```
--------------------------------
### Include MudBlazor CSS and JS
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/90 Tooling/10 Code Generation/README.md
Adds the necessary MudBlazor CSS and JavaScript links to the wwwroot/index.html file. This ensures MudBlazor's styling and functionality are loaded.
```text
+
+
```
--------------------------------
### Render List Items with Keys
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/11 DOM/README.md
Generate an unordered list with dynamically generated list items. Using the 'key' attribute is crucial for preserving list order during re-renders.
```fsharp
div {
h3 { "Some title." }
ul {
for item in 0..10 do
li {
key item
$"Item: {item}"
}
}
}
```
--------------------------------
### Adaptive Rendering - Avoid
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/13 Interactive Nodes/README.md
Illustrates an anti-pattern for adaptive rendering where static content is placed inside an `adapt` block, causing unnecessary re-renders. Prefer to keep static content outside of adaptive scopes.
```fsharp
// ❌ Avoid
adapt {
let! content = myAValue
$"Hello static string" // will re-render
div { "Hello static node" } // will re-render
p { // will re-render
div { // will re-render
$"Hello dynamic content {content}"// will re-render
}
}
}
```
--------------------------------
### Set up Nested Cascading Values in F#
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/43 Cascading Value/README.md
Demonstrates how to set up nested `CascadingValue` components. The inner `CascadingValue` with `Value 2` will be consumed because it is closer to the consumer.
```fsharp
let demo =
CascadingValue'' {
Name "MyContext"
Value 1
CascadingValue'' {
Name "MyContext"
Value 2 // This value will be consumed because it is more close to the consumer.
MyContext.Consume (fun x -> div {
$"data = {x}"
})
}
}
```
--------------------------------
### Create Manual Blazor Component Bindings
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/44 Working With Blazor/README.md
Define extension methods using `ComponentAttrBuilder()` to create a simplified API for using third-party or custom Blazor components within Fun.Blazor markup.
```fsharp
// This is an existing component it can be defined in C# or F#
type MyComponent with
static member create(value, onValueChanged) =
html.blazor (
ComponentAttrBuilder()
.Add((fun x -> x.Value), value)
.Add((fun x -> x.OnValueChangeCb), EventCallback(null, Action onValueChanged))
)
```
--------------------------------
### Adaptive Data Rendering with FunBlazorComponent
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/14 Classes and Functions/README.md
Use `FunBlazorComponent` to opt out of re-renders for event triggers, localizing updates to the `adapt` block. This is useful when fine-grained control over component updates is required.
```fsharp
type Demo() =
// Note this is not the same as *inherit FunComponent()*
inherit FunBlazorComponent()
let data = cval 0
override _.Render() = fragment {
h1 { "Static text" }
adapt {
let! data = data
// only this part will need to be used for computate for diff when data is changed
p { $"x = {data}" }
}
button {
onclick (fun _ -> data.Publish(fun value -> value + 1))
"Increase"
}
}
```
--------------------------------
### Component Hook for UI Logic
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/20 Get Started/README.md
Illustrates creating reusable UI logic hooks for tasks like asynchronous data loading or form management within components.
```fsharp
type IComponentHook with
member hook.TryLoadPosts(page) = task {
...
}
member hook.UseSettingsForm() =
hook
.UseAdaptiveForm({| Name = "foo"; ... |})
.AddValidators(...)
.AddValidators(...)
```
--------------------------------
### Async Service Injection with html.inject
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/42 Dependency Injection/README.md
Demonstrates how `html.inject` supports asynchronous rendering. The provided function is called within the `OnInitializedAsync` lifecycle method of the Blazor component, allowing tasks to be invoked.
```fsharp
builder.Root.Add(html.inject(fun (asyncService) ->
async {
let! data = asyncService.GetDataAsync()
return div {
p {
text ($"Async Data: {data}")
}
}
}
))
```
--------------------------------
### F# Blazor CE to RenderFragment Delegate
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/10 About/README.md
Illustrates the conversion of a computation expression (CE) style F# code into a Blazor RenderFragment delegate, similar to Razor's output.
```fsharp
let demo =
div {
class' "cool"
}
```
```fsharp
let demo =
NodeRenderFragment(fun comp builder index -> // delegate
builder.OpenElement(index, "div")
bulder.AddAttribute(index + 1, "class", "cool")
builer.CloseElement()
index + 2
)
type NodeRenderFragment = delegate of root: IComponent * builder: RenderTreeBuilder * sequence: int -> int
```
--------------------------------
### F# and C# Counter Component Styles
Source: https://github.com/slaveoftime/fun.blazor/blob/master/README.md
Demonstrates two ways to implement a counter component in Fun.Blazor: a functional style using computation expressions and a class-based style inheriting from FunComponent.
```fsharp
let count = cval 0
let counter (str: string) = section {
h2 { "Counter: "; str }
adapt {
let! count, setCount = count.WithSetter()
button {
onclick (fun _ -> setCount (count + 1))
"Increase "; count
}
}
}
// Class style
type CountPage() =
inherit FunComponent()
let mutable count = 0
override _.Render() =
main {
h1 { "Counter Page" }
p { "hi here" }
button {
onclick (fun _ -> count <- count + 1)
"Increase "; count
}
counter "functional style"
}
```
--------------------------------
### Configure ProjectReference for Code Generation
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/90 Tooling/10 Code Generation/README.md
Add the FunBlazor="" attribute to a ProjectReference in your .fsproj file to enable code generation for a local C# project.
```xml
```
--------------------------------
### Create Basic HTML Element with Attributes
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/11 DOM/README.md
Define an HTML element with an ID and class using F# Computation Expressions. Note the use of 'class'' for reserved keywords.
```fsharp
let hello name = div {
id "my-id"
class' "my-class"
"Hello, "; name
}
hello "World!"
```
```html
Hello, World!
```
--------------------------------
### Render Content Based on Match Expression
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/11 DOM/README.md
Use a match expression to render different content based on a type. 'region' is recommended for dynamic content.
```fsharp
let element kind = div {
$"The element is: "
region {
match kind with
| Fantastic -> "Fantastic"
| Average -> "Average"
| WellItsSomething -> "Wel... it is something"
}
}
```
--------------------------------
### Define a Blazor Component in F#
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/44 Working With Blazor/README.md
Create a Blazor component in F# by inheriting from `FunComponent` and defining parameters, injected services, and event callbacks. The `Render` method defines the component's UI.
```fsharp
type MyComponent() =
inherit FunComponent() // This is required
[]
member val Logger: ILogger = Unchecked.defaultof<_> with get, set
[]
member val Value = "" with get, set
[]
member val OnValueChangeCb: EventCallback = Unchecked.defaultof<_> with get, set
override this.Render() = div {
$"Hello {this.Value}!"
textarea {
type' "text"
value this.Value
oninput (fun e ->
let value = unbox e.Value
this.Logger.LogDebug("Value Changed: {OldValue} -> {NewValue}", this.Value, value)
this.OnValueChangeCb.InvokeAsync value
)
}
}
```
--------------------------------
### Register Fun.Blazor Components and Custom Elements
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/80 Htmx/README.md
Register necessary services for Razor Components, Server-Side Blazor, and Fun.Blazor. This includes enabling custom element registration for real-time interactivity with WebSockets.
```fsharp
services.AddRazorComponents()
services.AddServerSideBlazor(fun options ->
// for using html.customElement
options.RootComponents.RegisterCustomElementForFunBlazor(Assembly.GetExecutingAssembly()))
services.AddFunBlazorServer()
...
app.MapRazorComponents()
app.MapRazorComponentsForSSR(Assembly.GetExecutingAssembly(), enableAntiforgery = true)
app.MapCustomElementsForSSR(Assembly.GetExecutingAssembly(), enableAntiforgery = true)
```
--------------------------------
### Add MudBlazor Package
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/90 Tooling/10 Code Generation/README.md
Adds the MudBlazor NuGet package to the Fun.Blazor project.
```bash
dotnet add package MudBlazor
```
--------------------------------
### Conditional Rendering with html.injectWithNoKey
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/42 Dependency Injection/README.md
Illustrates a scenario where `html.injectWithNoKey` might lead to unexpected behavior if used for conditional rendering. Blazor may treat different injected components as the same, preventing re-creation.
```fsharp
let comp1 = html.injectWithNoKey(fun (service) -> div { text "Comp1" })
let comp2 = html.injectWithNoKey(fun (service) -> div { text "Comp2" })
let comp =
div {
if some conditions then comp1
else comp2
}
```
--------------------------------
### Handle Input Events and Unbox Values
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/11 DOM/README.md
Capture input events and extract string values. For numeric inputs, unbox the string to an integer.
```fsharp
input {
placeholder "Write Something"
oninput(fun e ->
unbox e.Value |> printfn "New Value: '%s'"
)
}
input {
type' "number"
placeholder "Change Number"
type' InputTypes.number
oninput(fun e ->
unbox e.Value |> int |> printfn "New Value: '%i'"
)
}
```
--------------------------------
### Use Custom Blazor Component Bindings in Adaptive Markup
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/44 Working With Blazor/README.md
Integrate custom Blazor component bindings, created with `ComponentAttrBuilder`, into Fun.Blazor's adaptive markup for dynamic content rendering.
```fsharp
module Home =
open FSharp.Data.Adaptive
open Fun.Blazor
let view() =
article {
h1 { "This is a title" }
adapt {
let! value, setValue = cval("").WithSetter()
MyComponent.create(value, setValue)
$"the value is: {value.ToUpperInvariant()}"
}
}
```
--------------------------------
### Implement Custom Hook
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/41 Hook/README.md
Implement the defined custom hook interface, typically by wrapping an existing IComponentHook.
```fsharp
type MyCompHook (hook: IComponentHook) =
interface IMyCompHook with
...
```
--------------------------------
### Configure PackageReference for Code Generation
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/90 Tooling/10 Code Generation/README.md
Add the FunBlazor="" attribute to a PackageReference in your .fsproj file to enable default code generation settings for that package. This is useful for third-party Blazor components.
```xml
```
--------------------------------
### Register Hook Service with DI
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/41 Hook/README.md
Register your custom hook implementation with the Dependency Injection container. This allows it to be resolved by consumers.
```fsharp
services.AddHookService(MyCompHook)
```
--------------------------------
### Extending Style Builder with Custom Operations
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/12 Style/README.md
Defines custom operations like VStack, HStack, and EvenHStack to the StyleBuilder for reusable flexbox layouts.
```fsharp
[]
module StyleExtensions =
open Fun.Blazor.Internal
open Fun.Css.Internal
type StyleBuilder with
[]
member inline _.VStack([] comb: CombineKeyValue) =
comb
&&& css {
displayFlex
flexDirectionColumn
}
[]
member inline _.HStack([] comb: CombineKeyValue) = comb &&& css { displayFlex }
[]
member inline this.EvenHStack([] comb: CombineKeyValue) =
this.HStack comb &&& css { custom "justify-content" "space-evenly" }
```
--------------------------------
### Static Greeting Function
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/14 Classes and Functions/README.md
A simple F# function that returns a static paragraph element. This function will not re-execute unless its parent component re-renders.
```fsharp
let staticGreeting name =
p { $"Hello there, {name}!" }
```
--------------------------------
### Update Project File for MudBlazor Integration
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/90 Tooling/10 Code Generation/README.md
Modifies the .fsproj file to include the FunBlazor attribute on the MudBlazor PackageReference, enabling code generation for MudBlazor components.
```text
-
+
```
--------------------------------
### Consume a Custom Element with Parameters
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/82 Custom Elements/README.md
Consume a custom element using `html.customElement`. Pass component attributes using `ComponentAttrBuilder` and specify rendering behavior like `RenderAfter.InViewport`.
```fsharp
html.customElement (
ComponentAttrBuilder().Add((fun x -> x.post_id), post.Id.ToString()),
renderAfter = RenderAfter.InViewport
)
```
--------------------------------
### Avoid: Re-rendering Entire Node with html.watch
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/13 Interactive Nodes/README.md
This pattern should be avoided as it causes the entire node, including static content, to re-render when the observable changes. Use this only when absolutely necessary.
```fsharp
// ❌ Avoid
html.watch(
obs,
(fun value -> fragment {
$"Hello static string" // will re-render
div { "Hello static node" } // will re-render
p { // will re-render
div { // will re-render
$"Hello dynamic content {value}"// will re-render
}
}
}),
0)
```
--------------------------------
### F# Blazor Adaptive Model Counter
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/10 About/README.md
A basic counter component for Blazor that utilizes an adaptive model for state management.
```fsharp
{{Counter}}
```
--------------------------------
### F# Blazor Class Style Counter
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/10 About/README.md
A basic counter component implemented using a class-style approach in F# for Blazor.
```fsharp
{{CounterClassStyle}}
```
--------------------------------
### Handle Button Click Events
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/11 DOM/README.md
Attach synchronous and asynchronous event handlers to a button. Handlers can be of type 'EventArgs -> unit' or 'EventArgs -> Task'.
```fsharp
button {
onclick(fun e -> printfn "clicked")
"Click Me"
}
button {
onclick(fun e -> task {
do! Async.Sleep 1000
printfn "clicked"
})
"Click Me Task"
}
```
--------------------------------
### Define a Fun.Blazor Custom Component
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/82 Custom Elements/README.md
Define a custom Blazor component using the `[]` attribute. Components must inherit from `FunComponent` and can define parameters like `post_id`.
```fsharp
[]
type PostLikesSurvey() =
inherit FunComponent()
[]
member val post_id = "" with get, set
override this.Render() = fragment {
...
}
```
--------------------------------
### Isolating UI Updates with Adapt
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/50 Adaptive/About.md
Use the 'adapt' block to define a section of your UI that will re-render independently when its data source changes. This is useful for dynamic UI parts that are only affected by specific data updates.
```fsharp
let yourUI =
div {
style { ... }
... a lot of stuff
adapt {
let! msg = from store
// only below part will be rerendered.
div {
style { ... }
msg
}
}
... a lot of stuff
}
```
--------------------------------
### Define Hook Interface for Unit Testing
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/41 Hook/README.md
Define an interface for your custom hook to enable mocking and unit testing. This is the recommended approach for testable components.
```fsharp
type IMyCompHook =
abstract member DataCanBeShared: ...
abstract member LoadDataAfterRender: ...
```
--------------------------------
### Specify Blazor Rendering Modes
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/44 Working With Blazor/README.md
Configure the rendering mode for Blazor components using `RenderMode` options like InteractiveServer, InteractiveAuto, or InteractiveWebAssembly.
```fsharp
html.blazor(RenderMode.InteractiveServer)
```
```fsharp
html.blazor(RenderMode.InteractiveAuto)
```
```fsharp
html.blazor(RenderMode.InteractiveWebAssembly)
```
--------------------------------
### Include Lazy Blazor JavaScript Helper
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/82 Custom Elements/README.md
Include the `CustomElement.lazyBlazorJs()` helper script in the head of your HTML page to enable custom element functionality. This script is essential for Blazor to interact with custom elements.
```fsharp
html {
head {
...
CustomElement.lazyBlazorJs ()
}
body {
...
}
}
```
--------------------------------
### Blazor Official Style Router Configuration
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/16 Routing/README.md
Configures the Blazor router using the official style. It specifies the application assembly and defines how to handle found routes, linking them to a default layout.
```fsharp
type Routes() =
inherit FunComponent()
override _.Render() = Router'' {
AppAssembly(Assembly.GetExecutingAssembly())
Found(fun routeData -> RouteView'' {
RouteData routeData
DefaultLayout typeof
})
}
```
--------------------------------
### Define Blazor Component with Dynamic Dependency
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/21 Publish Trim/README.md
When defining custom Blazor components, use the DynamicDependency attribute on the default constructor to ensure the IL linker does not trim necessary members. This is crucial because Blazor uses strings to reference component parameters, which can lead to unintended trimming.
```fsharp
// The Blazor component
type Autocomplete<'T when 'T: equality>() =
inherit MudBaseInput<'T option>()
[]
member val Clearable = true with get, set
...
// The DSL for Fun.Blazor
type Autocomplete'<'T when 'T : equality> [>)>] () =
inherit DslInternals.MudBaseInputBuilder, 'T option>()
[]
member inline _.Clearable([] render: AttrRenderFragment, x: bool) = render ==> ("Clearable" => x)
...
```
--------------------------------
### Define a Blazor Component for Comments with Htmx Integration
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/80 Htmx/README.md
Defines a Blazor component `PostComment` that supports Htmx for form submissions. It includes parameters for `PostId` and `Comment`, and uses `html.createHiddenInputs` to persist component state within the form.
```fsharp
type PostComment() as this =
inherit FunComponent()
[]
member val PostId = Guid.Empty with get, set
[]
member val Comment = "" with get, set
override _.Render() = form {
hxSwap_outerHTML
hxPostComponent typeof
// You can use below method to save the current state in the form, so the browser will always have the latest state
// And later you can override it, because server will only take the last value for the same key
html.createHiddenInputs this
html.blazor ()
textarea {
name (nameof this.Comment)
value this.Comment
}
button {
type' InputTypes.Submit
"Add"
}
```
--------------------------------
### Class-based Counter with DI
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/14 Classes and Functions/README.md
A Blazor component implemented as a class inheriting from FunComponent. It utilizes mutable state and Blazor's default re-rendering behavior, including dependency injection for ILogger.
```fsharp
type Counter() =
inherit FunComponent()
let mutable counter = 0
[]
member val Logger: ILogger = Unchecked.defaultof<_> with get, set
override this.Render() =
fragment {
h1 { $"Count: {counter}" }
button {
onclick (fun _ ->
let newCount = counter + 1
this.Logger.LogDebug("Old Value: {count} - New Value: {newCount}", counter, newCount)
counter <- newCount
)
"Increment"
}
}
```
--------------------------------
### Using Region for Isolated Sequence Numbers
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/15 Region/README.md
Demonstrates how the 'region' construct isolates content, preventing its dynamic changes from affecting the sequence numbers of surrounding elements.
```fsharp
div {
// sequence 0
region { // sequence 1
// Below content's sequence number is isolated
if isLoading then
loader
}
div {
// sequence 2
"hi" // sequence 3
}
}
```
--------------------------------
### Static Counter Function (Non-rendering)
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/14 Classes and Functions/README.md
This F# function demonstrates a counter that does not update on interaction because functions are executed once. To achieve dynamic updates within functions, consider using 'Adaptive' values, 'Stores', or 'Observables'.
```fsharp
let staticGreeting()=
let mutable counter = 0
button {
onclick (fun _ -> counter <- counter + 1)
$"Click Me: {counter}"
}
```
--------------------------------
### Blazor Official Style Layout Component
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/16 Routing/README.md
Defines a main layout component for Blazor applications using the official style. It includes navigation and a placeholder for the main content, which is rendered by the Blazor router.
```fsharp
type MainLayout() as this =
inherit LayoutComponentBase()
let content = div {
nav {
// your nav code
}
main { this.Body }
}
override _.BuildRenderTree(builder) = content.Invoke(this, builder, 0) |> ignore
```
--------------------------------
### Extend Hooks with Custom Functionality
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/41 Hook/README.md
Extend the IComponentHook interface to add custom properties and methods. This approach is simpler if unit testing is not a concern.
```fsharp
type IComponentHook with
member hook.DataCanBeShared =
let store = hook.ServiceProvider.GetService()
store.CreateCVal(nameof hook.DataCanBeShared, LoadingState<...>.NotStartYet)
member hook.LoadDataAfterRender() =
hook.AddFirstAfterRenderTask(fun () ->
task {
hook.DataCanBeShared.Publish LoadingState.start
let! result = ... call some API
hook.DataCanBeShared.Publish (LoadingState.Loaded data)
}
)
```
--------------------------------
### Set Custom HTML Attributes
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/11 DOM/README.md
Define custom HTML attributes for an element by providing a string tuple to the builder.
```fsharp
section {
"my-attribute", "value"
}
```
--------------------------------
### Share Attributes Between Elements
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/11 DOM/README.md
Define reusable attribute sets using 'domAttr' to apply common attributes like class and custom data attributes to multiple elements.
```fsharp
module SharedAttrs =
let classAndData = domAttr {
class' "has-data"
"my-data", "123"
}
let someNode = div {
SharedAttrs.classAndData
"Some Node"
}
let otherNode = div {
SharedAttrs.classAndData
"Other Node"
}
```
--------------------------------
### Conditional Element Rendering with Region
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/11 DOM/README.md
Conditionally render content within an element using an if/else statement. The 'region' builder can improve performance and semantics for dynamic parts.
```fsharp
let element isVisible = div {
$"The element is: "
region {
if isVisible then "Visible"
else "Not Visible"
}
}
```
--------------------------------
### Append Existing List of Nodes
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/11 DOM/README.md
Add an existing list of nodes to a parent element using the 'childContent' operation.
```fsharp
ul {
childContent listOfNodes
}
```
--------------------------------
### Yield Nodes Directly
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/11 DOM/README.md
Insert nodes from an existing list directly into the parent element using 'yield!'.
```fsharp
ul {
yield! listOfNodes
}
```
--------------------------------
### Consume Custom Hook in Component
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/41 Hook/README.md
Inject and consume your custom hook service within a Blazor component. A new instance is created each time it's consumed.
```fsharp
let myComp =
html.inject (fun (hook: IComponentHook) ->
// Every time you consume this, it will create a new instance for you.
let myCompHook = hook.GetHookService()
...
)
```
--------------------------------
### Bind to Component Members
Source: https://github.com/slaveoftime/fun.blazor/blob/master/Docs/40 Advanced/44 Working With Blazor/README.md
Utilize `ComponentAttrBuilder()` for strongly-typed access to component properties and event callbacks. This is useful for binding data and handling events.
```fsharp
html.blazor (
ComponentAttrBuilder()
.Add((fun x -> x.Value), "Some value")
.Add((fun x -> x.OnValueChange), EventCallback(null, Action (fun v -> printfn $"{v}")))
)
```