### 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 link true link true ``` -------------------------------- ### 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}"))) ) ```