# Vitraux Reference Manual This manual explains the public surface and runtime model of Vitraux for .NET and front-end developers. It focuses on how mappings are declared in C#, how the library generates and invokes JavaScript to update the DOM, wire HTML events, configuration options, caching, and change tracking. --- ## Concepts at a Glance - ViewModel output mapping: declare how a .NET ViewModel projects into HTML elements. - ViewModel action mapping: declaratively wire HTML events to ViewModel methods using auto-generated JavaScript event handlers. - Generated JS: at startup, Vitraux generates two JS functions per ViewModel on the fly: an initialize function and an update function. - Element query strategies: control when/where DOM queries happen to resolve target elements. - Change tracking: send full data or only changes on Update. - Caching: reuse generated JS across sessions by version. - Trimmable & AOT-ready (Blazor WebAssembly): safe to enable PublishTrimmed and AOT; no runtime codegen or reflective serializers. - Loosely coupled HTML: your HTML stays plain; mapping targets are selected by IDs, selectors, templates, or URIs. --- ## Setup and Bootstrapping Vitraux integrates via DI and a build step to prepare JS functions for your mapped ViewModels. 1) Add services and optional configuration ```csharp _ = builder.Services .AddVitraux() // registers core services .AddConfiguration(() => new VitrauxConfiguration { UseShadowDom = true }) // optional; default: true .AddViewModelConfiguration() //Add your ViewModel configuration .AddActionParameterBinder() //Add your action binders .AddActionParameterBinderAsync() .AddViewModelConfiguration(); // or _ = builder.Services .AddVitraux() .AddDefaultConfiguration() //Add the default configuration: UseShadowDom = true .AddViewModel() //Register your ViewModel as a singleton .AddConfiguration() //Add your ViewModels configuration .AddActionParameterBinder() //Add your action binders .AddActionParameterBinderAsync() .AddViewModel() .AddConfiguration() ``` 2) Build Vitraux (generates/initializes JS functions) ```csharp await host.Services.BuildVitraux(); ``` 3) Resolve an `IViewUpdater` and call `Update` whenever you need to sync the UI ```csharp var updater = host.Services.GetRequiredService>(); await updater.Update(vm); //ViewModel configured using .AddViewModelConfiguration() //or await updater.Update(); //ViewModel registered as singleton using .AddViewModel(). ``` **⚠️ Important** Actions (see below) are always attached to the ViewModel instance currently active in the UI: (the instance passed to the last `Update(vm)`). When using AddViewModel(), the singleton instance is used automatically. ### APIs involved - `AddVitraux(this IServiceCollection)` registers internal subsystems. - `AddConfiguration(Func)` or `AddDefaultConfiguration()` provides global options passed to the JS runtime at build time. - `AddModelConfiguration()` wires a mapping and registers an updater, event handlers and trackers for that ViewModel. - `AddViewModel().AddConfiguration()` Register the ViewModel in the IoC container, then wires a mapping and registers an updater and trackers for that ViewModel. - `BuildVitraux(this IServiceCollection)` triggers code generation and initialization for each registered ViewModels. - `AddActionParameterBinder()` Register a custom action parameter binder to be called on registered HTML events. Used to parse HTML parameters and call the appropriate ViewModel method. --- ## ViewModel Mapping DSL You declare mapping inside an `IViewModelConfiguration` implementation via the provided fluent builder (`IModelMapper`). Just an example: ```csharp public class PetOwnerConfiguration : IViewModelConfiguration { public ConfigurationBehavior ConfigurationBehavior { get; } = new() { ActionRegistrationStrategy = ActionRegistrationStrategy.OnlyOnceAtStart, QueryElementStrategy = QueryElementStrategy.OnlyOnceAtStart, TrackChanges = true, AutoGeneratedFunctionCaching = AutoGeneratedFunctionCaching.ByVersion("1.0") }; public ModelMappingData ConfigureMapping(IModelMapper model) => model .MapActionAsync().FromInputs.ById("dropdown-petowners").On("change") // action mapping .AddParameterValue .AddParameter("numbers").FromParamInputs.ByQuery("input[type='number']") .AddParameter("extratext").FromParamInputs.ByQuery("input[type='text']") .MapValue(po => po.Name) // value mapping .ToElements.ById("petowner-name").ToContent .MapValue(po => po.HtmlComments) .ToElements.ByQuery(".comments").ToHtml .MapCollection(po => po.Pets) // collection mapping .ToTables.ById("petowner-pets") .PopulatingRows.ToTBody(0) .FromTemplate("petowner-pet-row") .MapValue(pet => pet.Name) .ToElements.ByQuery("[data-id='pet-name']").ToContent .MapValue(pet => pet.DateOfBirth) .ToElements.ByQuery("[data-id='pet-date-of-birth']").ToContent .EndCollection .Data; } ``` ### Map values - `MapValue(Func)` starts a value mapping chain. - As Vitraux doesn't bind properties the delegate signature is `Func`. The first generic parameter is the source `TViewModel` (or `TItem` in inner collections) and the last is any type `TValue`. So, the following `MapValue` would be valid for Vitraux although no one would do that: `MapValue(pet => Int32.Parse(pet.DateOfBirth.ToString("yyyy")) + 2000)` - `.ToElements.ById(string)` or `.ByQuery(string)` selects target element(s). - `.ToContent` sets `textContent` via the built-in JS call. - `.ToHtml` sets `innerHTML`: Injected HTML is the user’s responsibility. - `.ToAttribute(string attribute)` sets/toggles the given attribute: - If the mapped value is boolean: `true` adds the attribute (presence-based), `false` removes it. Ideal for boolean attributes like `disabled`, `checked`, `hidden`. - Otherwise: sets the attribute to the stringified value, e.g., `href`, `src`, `data-*`. Advanced value targets: - `.Insert.FromTemplate(string templateId)|FromUri(Uri uri).ToChildren.ByQuery(string).ToContent|ToHtml|ToAttribute(...)` - Insert a fragment (from a `