### Complete Datastar MVC Setup for Controllers Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/servicesregistration.md Example demonstrating the setup for Datastar MVC support when using traditional controllers. This includes registering services and mapping controllers. ```csharp var builder = WebApplication.CreateBuilder(args); builder.Services .AddDatastar() .AddJsonOptions(options => options.PropertyNameCaseInsensitive = true) .AddDatastarMvc(); var app = builder.Build(); app.UseRouting(); app.MapControllers(); app.Run(); // In controller [ApiController] [Route("[controller]")] public class DataController : ControllerBase { private readonly IDatastarService _datastarService; public DataController(IDatastarService datastarService) { _datastarService = datastarService; } [HttpPost("update")] public async Task Update([FromSignals] UserData user) { var html = $"
{user.Name}
"; await _datastarService.PatchElementsAsync(html); return Ok(); } } ``` -------------------------------- ### Complete Datastar MVC Setup for Minimal APIs Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/servicesregistration.md A comprehensive example for setting up Datastar with custom JSON options and MVC support in minimal APIs. Includes examples for basic endpoints, signal handling, and [FromSignals] attribute usage. ```csharp var builder = WebApplication.CreateBuilder(args); // Register Datastar with custom JSON options builder.Services .AddDatastar() .AddJsonOptions(options => { options.PropertyNameCaseInsensitive = true; options.Converters.Add(new JsonStringEnumConverter()); }) .AddDatastarMvc(); // For [FromSignals] support var app = builder.Build(); app.UseStaticFiles(); // Basic endpoint app.MapGet("/time", async (IDatastarService service) => { await service.PatchElementsAsync($"
{DateTime.Now:HH:mm:ss}
"); }); // With signals app.MapPost("/greet", async (IDatastarService service) => { var signals = await service.ReadSignalsAsync(); var html = $"

Hello, {signals?.Name}!

"; await service.PatchElementsAsync(html); }); // With FromSignals attribute app.MapPost("/submit", async ([FromSignals] FormData data) => { return Results.Ok(new { received = data }); }); app.Run(); record GreetSignals(string Name); record FormData(string Title, string Content); ``` -------------------------------- ### PatchElementsOptions Examples Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/types.md Examples demonstrating how to configure PatchElementsOptions with different ElementPatchMode values. ```csharp // Replace inner content var options = new PatchElementsOptions { Selector = "#container", PatchMode = ElementPatchMode.Inner }; // Append to list var options = new PatchElementsOptions { Selector = "#items", PatchMode = ElementPatchMode.Append }; ``` -------------------------------- ### Minimal Datastar Setup Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/README.md Basic setup for a Datastar application. This registers Datastar services and sets up a minimal endpoint for patching elements. ```csharp var builder = WebApplication.CreateBuilder(args); builder.Services.AddDatastar(); var app = builder.Build(); app.MapPost("/update", async (IDatastarService service) => { await service.PatchElementsAsync("
Updated!
"); }); app.Run(); ``` -------------------------------- ### Complete Datastar .NET Configuration Example Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/configuration.md Sets up Datastar services with custom JSON serialization options and enables [FromSignals] model binding for MVC. Includes example endpoints for patching elements, signals, and executing scripts, as well as handling form submissions with [FromSignals] binding. ```csharp using System.Text.Json; using System.Text.Json.Serialization; using StarFederation.Datastar.DependencyInjection; using StarFederation.Datastar.ModelBinding; var builder = WebApplication.CreateBuilder(args); // Configure Datastar with all options builder.Services .AddDatastar() .AddJsonOptions(options => { // Property naming options.PropertyNameCaseInsensitive = true; options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; // Formatting options.WriteIndented = false; options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; // Converters options.Converters.Add(new JsonStringEnumConverter()); options.Converters.Add(new JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)); }) .AddDatastarMvc(); // Enable [FromSignals] model binding var app = builder.Build(); app.UseStaticFiles(); // Example endpoint with configuration options app.MapPost("/stream-demo", async (IDatastarService service) => { // Patch elements await service.PatchElementsAsync( "
Processing...
", new PatchElementsOptions { Selector = "#result", PatchMode = ElementPatchMode.Replace, UseViewTransition = true, Retry = TimeSpan.FromSeconds(2) } ); // Simulate work await Task.Delay(1000); // Update signals await service.PatchSignalsAsync( new { status = "complete", timestamp = DateTime.UtcNow }, new PatchSignalsOptions { OnlyIfMissing = false } ); // Execute script await service.ExecuteScriptAsync( "console.log('Demo complete');", new ExecuteScriptOptions { AutoRemove = true } ); }); // Example with [FromSignals] binding app.MapPost("/form-submit", async ([FromSignals] FormData form) => { return Results.Ok(new { received = form }); }); app.Run(); record FormData(string Title, string Content); ``` -------------------------------- ### Add Datastar Package Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/README.md Install the Datastar NuGet package using the .NET CLI. ```bash dotnet add package StarFederation.Datastar ``` -------------------------------- ### Full Example: Updating a Form Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/patchelementsoptions.md Demonstrates a complete example of using PatchElementsOptions within an ASP.NET Core endpoint to update a form in the DOM. ```APIDOC ## Full Example ```csharp app.MapPost("/update-form", async (IDatastarService datastarService) => { var signals = await datastarService.ReadSignalsAsync(); var options = new PatchElementsOptions { Selector = "#formContainer", PatchMode = ElementPatchMode.Replace, UseViewTransition = true, EventId = Guid.NewGuid().ToString(), Retry = TimeSpan.FromSeconds(2) }; var newHtml = $"""
"""; await datastarService.PatchElementsAsync(newHtml, options); }); ``` ``` -------------------------------- ### ExecuteScriptAsync Usage Examples Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/executescriptoptions.md Demonstrates various ways to use the `IDatastarService.ExecuteScriptAsync` method with different `ExecuteScriptOptions` configurations. ```APIDOC ## ExecuteScriptAsync Examples ### Simple execution with auto-removal ```csharp var options = new ExecuteScriptOptions(); await datastarService.ExecuteScriptAsync("alert('User created successfully!');", options); ``` ### Persistent script that stays in DOM ```csharp var options = new ExecuteScriptOptions { AutoRemove = false }; await datastarService.ExecuteScriptAsync("// Persistent script content", options); ``` ### Script with custom type attribute ```csharp var options = new ExecuteScriptOptions { Attributes = new[] { new KeyValuePair("type", "module") } }; await datastarService.ExecuteScriptAsync("// Module script content", options); ``` ### Script with multiple custom attributes ```csharp var options = new ExecuteScriptOptions { AutoRemove = false, Attributes = new[] { new KeyValuePair("type", "module"), new KeyValuePair("async", "async"), new KeyValuePair("crossorigin", "anonymous") } }; await datastarService.ExecuteScriptAsync("// Script with multiple attributes", options); ``` ### Script with event ID ```csharp var options = new ExecuteScriptOptions { EventId = "init-analytics", AutoRemove = true }; await datastarService.ExecuteScriptAsync("// Script with event ID", options); ``` ``` -------------------------------- ### PatchElementsOptions Usage Examples Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/patchelementsoptions.md Illustrates various ways to instantiate and configure PatchElementsOptions for different patching scenarios. ```APIDOC ## Usage ```csharp // Minimal: patch with no selector (client uses default) var options = new PatchElementsOptions(); // Target a specific element with default settings var options = new PatchElementsOptions { Selector = "#main" }; // Append to a list with view transition var options = new PatchElementsOptions { Selector = "#itemList", PatchMode = ElementPatchMode.Append, UseViewTransition = true }; // Insert SVG content var options = new PatchElementsOptions { Selector = "svg", Namespace = PatchElementNamespace.Svg, PatchMode = ElementPatchMode.Inner }; // Scoped view transition var options = new PatchElementsOptions { Selector = ".modal", UseViewTransition = true, ViewTransitionSelector = ".modal-content" }; ``` ``` -------------------------------- ### GET Request with Query Parameter Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/fromsignalsattribute.md Demonstrates binding signals passed via the 'datastar' query parameter for GET requests. ```APIDOC ## GET /api/search ### Description Performs a search based on 'query' and 'page' parameters passed within the 'datastar' query parameter. ### Method GET ### Endpoint /api/search ### Parameters #### Path Parameters None #### Query Parameters - **datastar** (string) - Required - A JSON string containing 'query' and 'page' parameters. - **query** (string) - The search query. - **page** (int) - The page number for search results. ### Request Example ``` GET /api/search?datastar={"query":"test","page":1} ``` ### Response #### Success Response (200) - **results** (array) - A list of search results matching the query. #### Response Example ```json [ { "title": "Sample Result 1" }, { "title": "Another Result" } ] ``` ``` -------------------------------- ### PatchSignalsAsync Examples Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/idatastarservice.md Demonstrates how to update signals using PatchSignalsAsync. Use the overload with PatchSignalsOptions to control update behavior, such as only updating if the signal is missing. ```csharp // Update a single signal await datastarService.PatchSignalsAsync(new { count = 42 }); ``` ```csharp // Only update if signal doesn't already exist on client await datastarService.PatchSignalsAsync( new { defaultName = "Guest" }, new PatchSignalsOptions { OnlyIfMissing = true } ); ``` ```csharp // Nested update await datastarService.PatchSignalsAsync(new { user = new { name = "Alice", role = "admin" } }); ``` -------------------------------- ### Constants Helper Class Usage Examples Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/extension-methods.md Demonstrates how to use the Consts.EnumToString method for converting different enum types to their string representations. ```csharp string mode = Consts.EnumToString(ElementPatchMode.Append); // Returns "append" string ns = Consts.EnumToString(PatchElementNamespace.Svg); // Returns "svg" string et = Consts.EnumToString(EventType.PatchSignals); // Returns "datastar-patch-signals" ``` -------------------------------- ### F# Handler Example for Server-Sent Events Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/fsharp-core.md An example of an F# handler that uses ServerSentEventGenerator to stream events, patch elements, read signals, and update signals. ```fsharp open StarFederation.Datastar.FSharp let handler (httpContextAccessor: IHttpContextAccessor) = task { let gen = ServerSentEventGenerator(httpContextAccessor) do! gen.StartServerEventStreamAsync() // Patch elements do! gen.PatchElementsAsync( """
Hello
""", { PatchElementsOptions.Defaults with Selector = ValueSome "#result" PatchMode = ElementPatchMode.Replace }, CancellationToken.None ) // Read signals let! signals = gen.ReadSignalsAsync() printfn "Received: %s" signals // Update signals do! gen.PatchSignalsAsync( Signals.create "{\"status\": \"done\"}", PatchSignalsOptions.Defaults, CancellationToken.None ) } ``` -------------------------------- ### Full PatchElementsAsync Example Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/patchelementsoptions.md Demonstrates a complete usage of PatchElementsAsync with detailed PatchElementsOptions, including selector, patch mode, view transition, event ID, and retry duration. ```csharp app.MapPost("/update-form", async (IDatastarService datastarService) => { var signals = await datastarService.ReadSignalsAsync(); var options = new PatchElementsOptions { Selector = "#formContainer", PatchMode = ElementPatchMode.Replace, UseViewTransition = true, EventId = Guid.NewGuid().ToString(), Retry = TimeSpan.FromSeconds(2) }; var newHtml = $"""
"""; await datastarService.PatchElementsAsync(newHtml, options); }); ``` -------------------------------- ### Execute Persistent Global Setup Script Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/executescriptoptions.md Execute a script to set up global JavaScript variables or functions. Set AutoRemove to false to keep the script in the DOM. ```csharp await datastarService.ExecuteScriptAsync( """ window.myApp = { version: '1.0', onReady: function() { console.log('Ready'); } }; """, new ExecuteScriptOptions { AutoRemove = false } ); ``` -------------------------------- ### Datastar Setup with Model Binding Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/README.md Configure Datastar to use MVC features for model binding. This enables the use of `[FromSignals]` attribute for controller parameters. ```csharp builder.Services.AddDatastar().AddDatastarMvc(); app.MapPost("/form", async ([FromSignals] FormData data) => { return Results.Ok(data); }); ``` -------------------------------- ### PatchElementsOptions Configuration Example Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/configuration.md Defines options for patching elements asynchronously, including CSS selectors, patch modes, view transition settings, and retry intervals. ```csharp var options = new PatchElementsOptions { Selector = "#main", PatchMode = ElementPatchMode.Append, UseViewTransition = true, ViewTransitionSelector = ".transition-scope", Namespace = PatchElementNamespace.Html, EventId = "patch-001", Retry = TimeSpan.FromSeconds(2) }; ``` -------------------------------- ### Execute Simple JavaScript Alert Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/idatastarservice.md Executes a simple JavaScript alert in the browser. This is a basic example of sending a script for execution. ```csharp await datastarService.ExecuteScriptAsync("alert('Hello from server!');"); ``` -------------------------------- ### GET Request with Query Parameter Binding Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/fromsignalsattribute.md For GET requests, signals are passed via the 'datastar' query parameter. The model binder automatically deserializes this JSON into the specified parameters. ```csharp // GET /api/search?datastar={"query":"test","page":1} [HttpGet("/api/search")] public IActionResult Search( [FromSignals] string query, [FromSignals] int page ) => Ok(_results.Where(r => r.Title.Contains(query)).Skip((page - 1) * 10)); ``` -------------------------------- ### Patch Signals with Collections Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/patchsignalsoptions.md Patch signals by providing collections of serializable items. This example demonstrates patching with an array of integers. ```csharp await service.PatchSignalsAsync(new { items = new[] { 1, 2, 3 } }); ``` -------------------------------- ### PatchElementNamespace Usage Examples Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/types.md Demonstrates how to use PatchElementNamespace with PatchElementsOptions for patching SVG or MathML content. Requires an instance of IDatastarService. ```csharp // SVG content var options = new PatchElementsOptions { Selector = "svg", Namespace = PatchElementNamespace.Svg, PatchMode = ElementPatchMode.Inner }; await service.PatchElementsAsync(svgContent, options); // MathML content var options = new PatchElementsOptions { Namespace = PatchElementNamespace.MathMl }; await service.PatchElementsAsync(mathContent, options); ``` -------------------------------- ### Datastar MVC Support Setup Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/configuration.md Enables Model binding for the [FromSignals] attribute and controller support within an MVC application. This must be called after AddDatastar(). ```csharp builder.Services .AddDatastar() .AddDatastarMvc(); ``` -------------------------------- ### F# Instance Methods for Server and Element Operations Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/fsharp-core.md These instance methods delegate to static members with thread-safe queuing for operations like starting server event streams, patching elements, removing elements, patching signals, executing scripts, and reading signals. ```fsharp member this.StartServerEventStreamAsync(additionalHeaders, cancellationToken) : Task member this.PatchElementsAsync(elements, options, cancellationToken) : Task member this.RemoveElementAsync(selector, options, cancellationToken) : Task member this.PatchSignalsAsync(signals, options, cancellationToken) : Task member this.ExecuteScriptAsync(script, options, cancellationToken) : Task member this.ReadSignalsAsync(cancellationToken) : Task member this.ReadSignalsAsync<'T>(jsonSerializerOptions, cancellationToken) : Task> member this.GetSignalsStream() : Stream ``` -------------------------------- ### Register Datastar MVC for Minimal APIs Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/fromsignalsattribute.md Register Datastar services with `AddDatastarMvc()` for use with minimal APIs. This example shows how to bind a model from signals in a minimal API endpoint. ```csharp builder.Services.AddDatastar().AddDatastarMvc(); app.MapPost("/api/test", ([FromSignals] TestModel model) => Results.Ok(model)); ``` -------------------------------- ### Configure Datastar JSON Options Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/extension-methods.md Configures JSON serialization options for signals. Use this to customize how JSON is handled, for example, to enable case-insensitive property name matching. ```csharp public static IDatastarBuilder AddJsonOptions( this IDatastarBuilder datastarBuilder, Action configureOptions) ``` ```csharp builder.Services .AddDatastar() .AddJsonOptions(options => { options.PropertyNameCaseInsensitive = true; options.Converters.Add(new JsonStringEnumConverter()); }); ``` -------------------------------- ### Register Datastar Service Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/idatastarservice.md Register the Datastar service using dependency injection with the AddDatastar() extension method. This setup is required before using the IDatastarService. ```csharp var builder = WebApplication.CreateBuilder(args); builder.Services.AddDatastar(); var app = builder.Build(); app.MapGet("/example", async (IDatastarService datastarService) => { await datastarService.PatchElementsAsync("
Hello
"); }); ``` -------------------------------- ### Patch Signals in ASP.NET Core Endpoint Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/patchsignalsoptions.md Demonstrates patching signals within ASP.NET Core endpoints using IDatastarService. Includes examples for general updates, initialization without overwriting, and incrementing counters with event IDs. ```csharp app.MapPost("/form-submit", async (IDatastarService datastarService) => { var signals = await datastarService.ReadSignalsAsync(); // Update result without overwriting user's form input await datastarService.PatchSignalsAsync( new { submitResult = "Success!" }, new PatchSignalsOptions { OnlyIfMissing = false } ); }); app.MapGet("/init", async (IDatastarService datastarService) => { // Initialize defaults, but don't overwrite existing client values await datastarService.PatchSignalsAsync( new { theme = "dark", language = "en" }, new PatchSignalsOptions { OnlyIfMissing = true } ); }); app.MapGet("/increment-counter", async (IDatastarService datastarService) => { var options = new PatchSignalsOptions { EventId = "counter-" + DateTime.UtcNow.Ticks }; await datastarService.PatchSignalsAsync(new { counter = 42 }, options); }); ``` -------------------------------- ### Validate Signal Path Attributes Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/errors.md Examples of invalid and valid signal path formats for the FromSignals attribute. Ensure paths are dot-separated, segments contain only letters, numbers, or underscores, and avoid empty or leading/trailing segments. ```csharp // ❌ Invalid paths [FromSignals(Path = "user.")] // Trailing dot [FromSignals(Path = ".name")] // Leading dot [FromSignals(Path = "user..name")] // Double dot [FromSignals(Path = "user-name")] // Hyphen not allowed // ✅ Valid paths [FromSignals(Path = "user")] [FromSignals(Path = "user.name")] [FromSignals(Path = "user.address.city")] [FromSignals(Path = "user_name")] ``` -------------------------------- ### Get Signals Stream from Request Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/fsharp-core.md Creates a readable stream from signals present in the HTTP request. For GET/DELETE, it returns a MemoryStream of the query parameter value. For other methods, it returns the request body stream. ```fsharp static member GetSignalsStream(httpRequest: HttpRequest) : Stream ``` -------------------------------- ### Handling JsonException in Signal Deserialization Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/errors.md Ensure clients send valid JSON for signals. For POST requests, set the `Content-Type` to `application/json`. This example shows how to handle potential null data after deserialization. ```csharp // Ensure client sends valid JSON // For GET: properly encode query parameter // For POST: proper Content-Type: application/json header // Handle potential null [HttpPost("/api/endpoint")] public IActionResult Handle([FromSignals] Data? data) { if (data == null) { return BadRequest("Invalid signals"); } // Process data } ``` -------------------------------- ### Get Signals Stream Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/idatastarservice.md Retrieves a stream to read signals from the client request in JSON format. For GET requests, it returns a MemoryStream. For POST/PUT requests, it returns the request body stream. ```csharp var stream = datastarService.GetSignalsStream(); using var reader = new StreamReader(stream); string signalsJson = await reader.ReadToEndAsync(); ``` -------------------------------- ### Configure Datastar Services with Builder Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/types.md Demonstrates configuring Datastar services using the IDatastarBuilder, including adding JSON options and MVC support. It also shows how to access the underlying service collection. ```csharp var builder = WebApplication.CreateBuilder(args); var datastarBuilder = builder.Services .AddDatastar() .AddJsonOptions(options => options.PropertyNameCaseInsensitive = true) .AddDatastarMvc(); // Can access underlying services var services = datastarBuilder.Services; ``` -------------------------------- ### View Transition Animations Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/README.md Implement view transition animations by specifying a selector and enabling UseViewTransition in PatchElementsOptions. ```csharp await service.PatchElementsAsync( newHtml, new PatchElementsOptions { Selector = "#container", UseViewTransition = true } ); ``` -------------------------------- ### Multiple FromSignals in POST/PUT Requests (Correct Usage) Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/fromsignalsattribute.md Shows the correct way to bind multiple parameters from a single request body using `[FromSignals]` with a `Path` to specify different parts of the signals object. ```APIDOC ## POST /api/good ### Description This endpoint demonstrates the correct usage of multiple `[FromSignals]` parameters by specifying a `Path` to extract different parts of the request body. ### Method POST ### Endpoint /api/good ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **user** ([FromSignals(Path = "user")]) - Binds the 'user' object from the request body. - **meta** ([FromSignals(Path = "metadata")]) - Binds the 'metadata' object from the request body. ### Request Example ```json { "user": { "name": "John Doe" }, "metadata": { "timestamp": "2023-10-27T10:00:00Z" } } ``` ### Response #### Success Response (200) None #### Response Example None ``` -------------------------------- ### Execute Script After DOM Update Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/README.md After updating the DOM with PatchElementsAsync, execute a JavaScript script using ExecuteScriptAsync, for example, to focus an element. ```csharp await service.PatchElementsAsync(html); await service.ExecuteScriptAsync("document.querySelector('#form').focus();"); ``` -------------------------------- ### Create RemoveElementOptions Instances Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/removeelementoptions.md Demonstrates creating instances of RemoveElementOptions with various configurations, including default, view transition enabled, event ID tracking, and combined settings. ```csharp // Simple removal var options = new RemoveElementOptions(); // Removal with view transition animation var options = new RemoveElementOptions { UseViewTransition = true }; // With event ID tracking var options = new RemoveElementOptions { EventId = Guid.NewGuid().ToString() }; // Combined settings var options = new RemoveElementOptions { UseViewTransition = true, EventId = "remove-notification-1", Retry = TimeSpan.FromSeconds(3) }; ``` -------------------------------- ### Instance Methods Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/fsharp-core.md These instance methods are available on FSharp Core objects and delegate to static members, ensuring thread-safe queuing for operations. ```APIDOC ## Instance Methods All instance methods delegate to static members with thread-safe queuing: ```fsharp member this.StartServerEventStreamAsync(additionalHeaders, cancellationToken) : Task member this.PatchElementsAsync(elements, options, cancellationToken) : Task member this.RemoveElementAsync(selector, options, cancellationToken) : Task member this.PatchSignalsAsync(signals, options, cancellationToken) : Task member this.ExecuteScriptAsync(script, options, cancellationToken) : Task member this.ReadSignalsAsync(cancellationToken) : Task member this.ReadSignalsAsync<'T>(jsonSerializerOptions, cancellationToken) : Task> member this.GetSignalsStream() : Stream ``` Instance methods queue SSE events to ensure proper ordering when called from different contexts. ``` -------------------------------- ### Safely Get JsonElement Property by Name Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/extension-methods.md Safely retrieves a property from a JsonElement by its name. Returns null if the property is not found or the element is invalid. ```csharp public static JsonElement? Get(this JsonElement element, string name) ``` ```csharp var json = JsonDocument.Parse("{ \"name\":\"John\",\"age\":30 }"); var name = json.RootElement.Get("name"); // JsonElement with value "John" var missing = json.RootElement.Get("missing"); // null ``` -------------------------------- ### Handling Multiple [FromSignals] Parameters Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/errors.md Use the `Path` property for multiple `[FromSignals]` parameters in a POST/PUT request body to ensure correct consumption of the signal stream. Otherwise, only the first parameter will bind. ```csharp // ❌ Wrong - body consumed by first parameter [HttpPost("/api/bad")] public IActionResult Bad([FromSignals] First first, [FromSignals] Second second) { // second will be null (body already consumed) } // ✅ Correct - use Path for multiple extractions [HttpPost("/api/good")] public IActionResult Good( [FromSignals(Path = "first")] First first, [FromSignals(Path = "second")] Second second ) { // Both properly bound } ``` -------------------------------- ### OnlyIfMissing Behavior Example Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/patchsignalsoptions.md Illustrates how OnlyIfMissing = true prevents overwriting existing client signals. The 'userInput' signal is preserved because it already exists on the client. ```csharp // Client has signals: { userInput: "hello" } // Server sends: { userInput: "goodbye", defaultValue: "init" } // Result: { userInput: "hello", defaultValue: "init" } // userInput is NOT overwritten because it exists on client ``` -------------------------------- ### Server-Driven State Initialization with PatchSignalsAsync Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/patchsignalsoptions.md Use PatchSignalsAsync with OnlyIfMissing = true to provide default values when a page loads. This ensures that server-provided defaults do not overwrite values that the user may have already modified. ```csharp app.MapGet("/page", async (IDatastarService service) => { // Provide defaults, but don't overwrite if user already set these await service.PatchSignalsAsync( new { theme = "light", pageSize = 20, sortBy = "date" }, new PatchSignalsOptions { OnlyIfMissing = true } ); }); ``` -------------------------------- ### Create ExecuteScriptOptions Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/executescriptoptions.md Instantiate ExecuteScriptOptions to configure script execution. Default options enable auto-removal. ```csharp var options = new ExecuteScriptOptions(); ``` ```csharp var options = new ExecuteScriptOptions { AutoRemove = false }; ``` ```csharp var options = new ExecuteScriptOptions { Attributes = new[] { new KeyValuePair("type", "module") } }; ``` ```csharp var options = new ExecuteScriptOptions { AutoRemove = false, Attributes = new[] { new KeyValuePair("type", "module"), new KeyValuePair("async", "async"), new KeyValuePair("crossorigin", "anonymous") } }; ``` ```csharp var options = new ExecuteScriptOptions { EventId = "init-analytics", AutoRemove = true }; ``` -------------------------------- ### JsonElement Extensions - Get (by index) Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/extension-methods.md Safely retrieves an element from a JSON array by its index. Returns the element or null if the index is out of range or the element is null/undefined. ```APIDOC ## Get (by index) ### Description Safely gets an array element from a `JsonElement` by its index. ### Method `public static JsonElement? Get(this JsonElement element, int index)` ### Parameters #### Path Parameters - **index** (int) - Required - Array index ### Returns `JsonElement?` - The array element or null if out of range or element is null/undefined ### Usage ```csharp var json = JsonDocument.Parse("[1,2,3]"); var second = json.RootElement.Get(1); // JsonElement with value 2 var outOfRange = json.RootElement.Get(10); // null ``` ``` -------------------------------- ### Get JsonElement Value from Nested Path Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/extension-methods.md Retrieves a JsonElement from a nested JSON structure using a dot-separated path. Returns null if the path does not exist. ```csharp public static JsonElement? GetFromPath(this JsonElement jsonElement, string path) ``` ```csharp var json = JsonDocument.Parse(@"{ \"user\": { \"profile\": { \"name\": \"Alice\" } } }"); var name = json.RootElement.GetFromPath("user.profile.name"); // Returns JsonElement with value "Alice" ``` -------------------------------- ### Create PatchSignalsOptions Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/patchsignalsoptions.md Instantiate PatchSignalsOptions to configure signal patching behavior. Use properties like OnlyIfMissing, EventId, and Retry to control updates. ```csharp var options = new PatchSignalsOptions(); ``` ```csharp var options = new PatchSignalsOptions { OnlyIfMissing = true }; ``` ```csharp var options = new PatchSignalsOptions { EventId = Guid.NewGuid().ToString() }; ``` ```csharp var options = new PatchSignalsOptions { OnlyIfMissing = true, EventId = "init-defaults", Retry = TimeSpan.FromSeconds(2) }; ``` -------------------------------- ### StartServerEventStreamAsync Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/fsharp-core.md Initializes the HTTP response as a Server-Sent Event stream, setting necessary headers and preparing for streaming. ```APIDOC ## StartServerEventStreamAsync ### Description Initializes the HTTP response as a Server-Sent Event stream, setting necessary headers and preparing for streaming. ### Method static member ### Parameters #### Path Parameters - **httpResponse** (HttpResponse) - Required - The ASP.NET Core HttpResponse object - **additionalHeaders** (seq>) - Required - Custom HTTP headers to add - **cancellationToken** (CancellationToken) - Required - Cancellation token ### Behavior - Sets `Content-Type: text/event-stream` - Sets `Cache-Control: no-cache` - For HTTP/1.1: adds `Connection: keep-alive` - Merges custom headers (doesn't overwrite existing) - Flushes response to start streaming ``` -------------------------------- ### Safely Get JsonElement Array Element by Index Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/extension-methods.md Safely retrieves an element from a JsonElement array by its index. Returns null if the index is out of range or the element is invalid. ```csharp public static JsonElement? Get(this JsonElement element, int index) ``` ```csharp var json = JsonDocument.Parse("[1,2,3]"); var second = json.RootElement.Get(1); // JsonElement with value 2 var outOfRange = json.RootElement.Get(10); // null ``` -------------------------------- ### C# Backend: Signal Handling for Client Input and Output Source: https://github.com/starfederation/datastar-dotnet/blob/main/README.md This C# endpoint shows how to read client-provided signals, process them to update server-side state (e.g., output message), and then send updated signals back to the client. ```csharp public record MySignals { [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Input { get; init; } = null; [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Output { get; init; } = null; public string Serialize() => ... } // changeOutput - reads the signals, update the Output, and merge back app.MapPost("/changeOutput", async (IDatastarService datastarService) => ... { MySignals signals = await datastarService.ReadSignalsAsync(); MySignals newSignals = new() { Output = $"Your Input: {signals.Input}" }; await datastarService.PatchSignalsAsync(newSignals.Serialize()); }); ``` -------------------------------- ### Basic Datastar Service Registration Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/configuration.md Sets up the core Datastar services, including IDatastarService and IHttpContextAccessor. It also configures default JSON serialization and F# ServerSentEventGenerator. ```csharp var builder = WebApplication.CreateBuilder(args); builder.Services.AddDatastar(); var app = builder.Build(); ``` -------------------------------- ### JsonElement Extensions - Get (by property name) Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/extension-methods.md Safely retrieves a property from a JsonElement by its name. Returns the property's JsonElement or null if the property does not exist or the element is null/undefined. ```APIDOC ## Get (by property name) ### Description Safely gets a property from a `JsonElement` by its name. ### Method `public static JsonElement? Get(this JsonElement element, string name)` ### Parameters #### Path Parameters - **name** (string) - Required - Property name to retrieve ### Returns `JsonElement?` - The property value or null if not found or element is null/undefined ### Usage ```csharp var json = JsonDocument.Parse("{\"name\":\"John\",\"age\":30}"); var name = json.RootElement.Get("name"); // JsonElement with value "John" var missing = json.RootElement.Get("missing"); // null ``` ``` -------------------------------- ### Minimal PatchElementsOptions Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/patchelementsoptions.md Create a PatchElementsOptions instance with default settings. The client will use the default selector. ```csharp var options = new PatchElementsOptions(); ``` -------------------------------- ### Read Signals and Update DOM/State Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/README.md Read signals to get data, update the DOM using PatchElementsAsync, and then update signals with new processed state using PatchSignalsAsync. ```csharp var signals = await service.ReadSignalsAsync(); await service.PatchElementsAsync("
Received
"); await service.PatchSignalsAsync(new { processed = true }); ``` -------------------------------- ### F# Web Application with Server-Sent Events Source: https://github.com/starfederation/datastar-dotnet/blob/main/README.md This F# code sets up a basic web application using ASP.NET Core. It demonstrates how to use ServerSentEventGenerator to send date updates and remove elements from the client-side. It also shows how to handle POST requests to update signals based on client input. ```fsharp namespace HelloWorld open System open System.Text.Json open System.Threading.Tasks open Microsoft.AspNetCore.Builder open Microsoft.AspNetCore.Http open Microsoft.Extensions.DependencyInjection open Microsoft.Extensions.Hosting open StarFederation.Datastar.FSharp module Program = let message = "Hello, world!" [] type MySignals = { input:string; output:string } [] let main args = let builder = WebApplication.CreateBuilder(args) builder.Services.AddHttpContextAccessor(); let app = builder.Build() app.UseStaticFiles() app.MapGet("/displayDate", Func(fun ctx -> task { do! ServerSentEventGenerator.StartServerEventStreamAsync(ctx.HttpContext.Response) let today = DateTime.Now.ToString("%y-%M-%d %h:%m:%s") do! ServerSentEventGenerator.PatchElementsAsync(ctx.HttpContext.Response, $"
{today}
") })) app.MapGet("/removeDate", Func(fun ctx -> task { do! ServerSentEventGenerator.StartServerEventStreamAsync(ctx.HttpContext.Response) do! ServerSentEventGenerator.RemoveElementAsync (ctx.HttpContext.Response, "#date") })) app.MapPost("/changeOutput", Func(fun ctx -> task { do! ServerSentEventGenerator.StartServerEventStreamAsync(ctx.HttpContext.Response) let! signals = ServerSentEventGenerator.ReadSignalsAsync(ctx.HttpContext.Request) let signals' = signals |> ValueOption.defaultValue { input = ""; output = "" } do! ServerSentEventGenerator.PatchSignalsAsync(ctx.HttpContext.Response, JsonSerializer.Serialize( { signals' with output = $"Your input: {signals'.input}" } )) })) app.Run() 0 ``` -------------------------------- ### GetSignalsStream Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/idatastarservice.md Retrieves a readable stream of signals from the client request, encoded in JSON format. For GET requests, it returns a MemoryStream. For POST/PUT requests, it returns the request body stream. ```APIDOC ## GetSignalsStream ### Description Returns a stream to read signals from the client request in JSON format. ### Signature ```csharp Stream GetSignalsStream() ``` ### Returns `Stream` - Readable stream containing JSON-encoded signals. For GET requests, returns a `MemoryStream`. For POST/PUT/etc, returns the request body stream. ### Example ```csharp var stream = datastarService.GetSignalsStream(); using var reader = new StreamReader(stream); string signalsJson = await reader.ReadToEndAsync(); ``` ``` -------------------------------- ### PatchSignalsOptions Configuration Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/configuration.md Configure options for patching signals. Set `OnlyIfMissing` to true to update only if the signal does not exist on the client. `EventId` is for deduplication, and `Retry` sets the SSE reconnection interval. ```csharp var options = new PatchSignalsOptions { OnlyIfMissing = true, EventId = "signals-001", Retry = TimeSpan.FromSeconds(2) }; ``` -------------------------------- ### Get and Deserialize JsonElement Value from Path Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/extension-methods.md Retrieves and deserializes a value from a nested JSON structure using a dot-separated path. Requires the target type and optional serialization options. ```csharp public static object? GetValueFromPath( this JsonElement jsonElement, string path, Type jsonElementType, JsonSerializerOptions jsonSerializerOptions) ``` ```csharp var json = JsonDocument.Parse(@"{ \"user\": { \"name\": \"Bob\", \"age\": 25 } }"); var obj = json.RootElement.GetValueFromPath( "user", typeof(UserModel), new JsonSerializerOptions() ); // Returns UserModel instance var age = json.RootElement.GetValueFromPath( "user.age", typeof(int), new JsonSerializerOptions() ); // Returns integer 25 ``` -------------------------------- ### Execute JavaScript on Client Source: https://github.com/starfederation/datastar-dotnet/blob/main/_autodocs/api-reference/fsharp-core.md Sends JavaScript code to be executed in the browser. The script is sent without `