### Creating Navigation Links and Action Lists Source: https://context7.com/unops/uimetadataframework/llms.txt Demonstrates how to create clickable links to open other forms or trigger actions. `FormLink` can open forms in modals or as new pages, while `ActionList` groups multiple `FormLink`s. ```csharp using UiMetadataFramework.Basic.Output.ActionList; using UiMetadataFramework.Basic.Output.FormLink; using UiMetadataFramework.Core.Binding; public class UserListRow { [OutputField(Label = "Name")] public string Name { get; set; } = null!; // Renders as a clickable "Edit" link that opens the EditUser form [OutputField(Label = "Edit")] public FormLink? EditLink { get; set; } // Renders as a set of action buttons [OutputField(Label = "Actions")] public ActionList? Actions { get; set; } } // Building the output in the form handler: protected override ListUsersResponse Handle(ListUsersRequest request) { var rows = GetUsers().Select(u => new UserListRow { Name = u.Name, EditLink = new FormLink { Form = "my-app/edit-user", Label = "Edit", Action = FormLinkActions.OpenModal, InputFieldValues = new Dictionary { ["UserId"] = u.Id } }, Actions = new ActionList( new FormLink { Form = "my-app/delete-user", Label = "Delete", Action = FormLinkActions.Submit, InputFieldValues = new Dictionary { ["UserId"] = u.Id } }, new FormLink { Form = "my-app/view-user", Label = "View details", Action = FormLinkActions.Open, InputFieldValues = new Dictionary { ["UserId"] = u.Id } } ) }).ToList(); return new ListUsersResponse { Users = rows }; } ``` -------------------------------- ### Dropdown Input with Static and Enum Sources Source: https://context7.com/unops/uimetadataframework/llms.txt Demonstrates creating dropdown inputs using both static inline sources and enum types. Ensure the source class implements `IDropdownInlineSource` or uses `EnumSource` for enums. ```csharp using UiMetadataFramework.Basic.Inputs.Dropdown; using UiMetadataFramework.Core.Binding; // Static list source public class StatusSource : IDropdownInlineSource { public IEnumerable GetItems() => new[] { new DropdownItem("Active", "active"), new DropdownItem("Inactive", "inactive"), new DropdownItem("Pending", "pending") }; } // Enum source (auto-detected; each enum member becomes a DropdownItem) public enum UserRole { Admin, Editor, Viewer } public class FilterUsersRequest : IRequest { // Renders as a dropdown populated by StatusSource [InputField(Label = "Status")] [Dropdown(typeof(StatusSource))] public DropdownValue? Status { get; set; } // Renders as a dropdown populated from the UserRole enum [InputField(Label = "Role")] [Dropdown(typeof(EnumSource))] public DropdownValue? Role { get; set; } } // On the client the component receives: // { "type": "dropdown", "customProperties": { "source": "StatusSource" } } // The frontend calls the source endpoint and receives: // [{ "value": "active", "label": "Active" }, ...] ``` -------------------------------- ### Registering Assemblies with MetadataBinder Source: https://context7.com/unops/uimetadataframework/llms.txt Use MetadataBinder to map .NET types to UI components. Register assemblies containing your component bindings and custom components. ```csharp using System.Reflection; using UiMetadataFramework.Basic.Server; using UiMetadataFramework.Core.Binding; // 1. Create a binder (uses DefaultDependencyInjectionContainer internally) var binder = new MetadataBinder(); // 2. Register the assembly that contains your ComponentBinding implementations. // UiMetadataFramework.Basic provides bindings for: number, text, boolean, // datetime, password, textarea, dropdown, typeahead, table, paginated-data, // formlink, action-list, inline-form, paginator. binder.RegisterAssembly(typeof(UiMetadataFramework.Basic.Properties).Assembly); // 3. Register your own application assembly if it defines custom components. binder.RegisterAssembly(Assembly.GetExecutingAssembly()); // 4. Manually build form metadata (used by FormRegister internally). var metadata = binder.BuildForm(); Console.WriteLine(metadata.Id); // "my-app/add-numbers" Console.WriteLine(metadata.InputFields.Count); // 2 Console.WriteLine(metadata.OutputFields.Count); // 1 // 5. Look up the component type name for a .NET type var componentType = binder.Inputs.Bindings.GetBinding(typeof(string)).ComponentType; // "text" ``` -------------------------------- ### Define a Form with Input and Output Fields in C# Source: https://github.com/unops/uimetadataframework/blob/develop/README.md Use this C# code to define a server-side form with input and output fields. UIMF uses .NET reflection to generate metadata from this definition. ```csharp [Form(Label = "Add 2 numbers", SubmitButtonLabel = "Submit", PostOnLoad = false)] public class AddNumbers : IForm { public class Response : FormResponse { [OutputField(Label = "Result of your calculation")] public long Result { get; set; } } public class Request : IRequest { [InputField(Label = "First number")] public int Number1 { get; set; } [InputField(Label = "Second number")] public int Number2 { get; set; } } public Response Handle(Request message) { return new Response { Result = message.Number1 + message.Number2 }; } } ``` -------------------------------- ### Decorating a Form Class with [FormAttribute] Source: https://context7.com/unops/uimetadataframework/llms.txt Use the [Form] attribute to mark a class as a UIMF form endpoint. Configure its client-facing label, ID, and auto-post behavior. This attribute is essential for metadata generation. ```csharp using MediatR; using UiMetadataFramework.Basic.Server; using UiMetadataFramework.Core.Binding; // [Form] configures how the client renders and behaves with this endpoint. [Form( Id = "my-app/add-numbers", // optional explicit ID; defaults to fully-qualified type name Label = "Add Two Numbers", // human-readable form title PostOnLoad = false, // do not auto-submit when opened PostOnLoadValidation = true, // validate inputs on auto-submit (no-op when PostOnLoad = false) CloseOnPostIfModal = true // auto-close modal after submission )] public class AddNumbers : Form { public class Request : IRequest { [InputField(Label = "First number", Required = true)] public int Number1 { get; set; } [InputField(Label = "Second number", Required = true)] public int Number2 { get; set; } } public class Response : FormResponse { [OutputField(Label = "Result")] public long Result { get; set; } } protected override Response Handle(Request request) => new Response { Result = request.Number1 + request.Number2 }; } // Resulting JSON metadata sent to the client: // { // "id": "my-app/add-numbers", // "label": "Add Two Numbers", // "postOnLoad": false, // "inputFields": [ // { "id": "Number1", "label": "First number", "type": "number", "required": true, "hidden": false }, // { "id": "Number2", "label": "Second number", "type": "number", "required": true, "hidden": false } // ], // "outputFields": [ // { "id": "Result", "label": "Result", "type": "number", "hidden": false } // ] // } ``` -------------------------------- ### Client-Side Form Handling with TypeScript Source: https://context7.com/unops/uimetadataframework/llms.txt Utilize the uimf-core npm package to hydrate JSON metadata into class instances for client-side form rendering and interaction. This includes fetching metadata, rendering forms, and submitting data. ```typescript import { FormMetadata, InputFieldMetadata, OutputFieldMetadata, FormResponse, FormLink } from "uimf-core"; // Fetch form metadata from the server (e.g. GET /api/form/metadata?id=my-app/add-numbers) async function loadForm(formId: string): Promise { const json = await fetch(`/api/form/metadata?id=${formId}`).then(r => r.json()); return new FormMetadata(json); } // Render the form using metadata async function renderForm(formId: string) { const form = await loadForm(formId); console.log(`Form: ${form.label}`); // "Add Two Numbers" console.log(`PostOnLoad: ${form.postOnLoad}`); // false // Iterate input fields to build UI controls for (const field of form.inputFields) { // field.type → "number", "text", "dropdown", "typeahead", "paginator", etc. // field.label, field.required, field.hidden, field.orderIndex console.log(`Input: ${field.id} (${field.type}) required=${field.required}`); // Read component-specific config from customProperties const dropdownSource = field.getCustomProperty("source"); // e.g. for dropdowns } // Iterate output fields for (const field of form.outputFields) { console.log(`Output: ${field.id} (${field.type}) hidden=${field.hidden}`); // For paginated-data, columns are also OutputFieldMetadata instances if (field.type === "paginated-data" && field.customProperties?.columns) { for (const colName in field.customProperties.columns) { const col: OutputFieldMetadata = field.customProperties.columns[colName]; console.log(` Column: ${col.id} label=${col.label}`); } } } } // Submit the form and handle the response async function submitForm(formId: string, requestData: object): Promise { const responseJson = await fetch(`/api/form/run`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ form: formId, request: requestData }) }).then(r => r.json()); // response.metadata.handler tells the client how to render the result: // "message" → show a notification // "redirect" → navigate to response.Form with InputFieldValues // "reload" → reinitialize the entire client if (responseJson.metadata?.handler === "redirect") { const redirectForm: string = responseJson.Form; const inputs: Record = responseJson.InputFieldValues ?? {}; await renderForm(redirectForm); // navigate } return responseJson as FormResponse; } ``` -------------------------------- ### Typeahead Input with Remote Source Source: https://context7.com/unops/uimetadataframework/llms.txt Shows how to implement a typeahead input that uses a remote source for filtering results. The `ITypeaheadRemoteSource` interface marks the source class, which is queried by the client. ```csharp using UiMetadataFramework.Basic.Inputs.Typeahead; using UiMetadataFramework.Basic.Inputs.Dropdown; using UiMetadataFramework.Core.Binding; // Remote source marker (client calls the server form associated with this type) public class UserSearchSource : ITypeaheadRemoteSource { } public class AssignTaskRequest : IRequest { // Renders as a typeahead; the client queries UserSearchSource with the typed text [InputField(Label = "Assign to user", Required = true)] [Typeahead(typeof(UserSearchSource))] public TypeaheadValue? AssigneeId { get; set; } } // TypeaheadValue carries the selected value and its display label public class AssignTaskResponse : FormResponse { [OutputField(Label = "Assignment confirmed")] public string? Confirmation { get; set; } } // The handler reads AssigneeId.Value (the int key) and AssigneeId.Label (display text): protected override AssignTaskResponse Handle(AssignTaskRequest request) { var userId = request.AssigneeId?.Value; // e.g. 42 var userName = request.AssigneeId?.Label; // e.g. "Alice" return new AssignTaskResponse { Confirmation = $ ``` -------------------------------- ### Registering and Retrieving Forms with FormRegister Source: https://context7.com/unops/uimetadataframework/llms.txt FormRegister manages form definitions keyed by form ID. Register forms by assembly or individually, and retrieve form information. ```csharp using System.Reflection; using UiMetadataFramework.Basic.Server; using UiMetadataFramework.Core.Binding; var binder = new MetadataBinder(); binder.RegisterAssembly(typeof(UiMetadataFramework.Basic.Properties).Assembly); var register = new FormRegister(binder); // Register all forms from the application assembly register.RegisterAssembly(Assembly.GetExecutingAssembly()); // Or register a single form explicitly register.RegisterForm(typeof(AddNumbers)); // Retrieve metadata by form ID FormInfo? info = register.GetFormInfo("my-app/add-numbers"); if (info != null) { Console.WriteLine(info.Metadata.Label); // "Add Two Numbers" Console.WriteLine(info.RequestType.FullName); // "AddNumbers+Request" Console.WriteLine(info.ResponseType.FullName); // "AddNumbers+Response" Console.WriteLine(info.Metadata.InputFields[0].Id); // "Number1" } // Iterate all registered forms (e.g. to expose a metadata catalogue endpoint) foreach (var formInfo in register.RegisteredForms) { Console.WriteLine($"{formInfo?.Metadata.Id}: {formInfo?.Metadata.Label}"); } ``` -------------------------------- ### Register Custom ComponentBinding Source: https://context7.com/unops/uimetadataframework/llms.txt Subclass ComponentBinding to define custom UI components. Register the assembly containing your binding using MetadataBinder.RegisterAssembly to enable auto-discovery. The metadataFactory can return custom properties for the component. ```csharp using UiMetadataFramework.Core.Binding; // Server-side type that the component renders public class RichTextValue { public string? Html { get; set; } } // Custom metadata factory for the component public class RichTextMetadataFactory : IMetadataFactory { public object? CreateMetadata( Type baseType, Type? derivedType, IComponentBinding binding, MetadataBinder binder, ComponentConfigurationAttribute[] configurations) { // return any object — it will be serialized into component.customProperties return new { toolbar = "full", allowImages = true }; } } // Binding: maps RichTextValue → "rich-text" component public class RichTextOutputBinding : ComponentBinding { public RichTextOutputBinding() : base( category: MetadataBinder.ComponentCategories.Output, serverType: typeof(RichTextValue), componentType: "rich-text", metadataFactory: typeof(RichTextMetadataFactory)) { } } // Usage in a form public class ArticleResponse : FormResponse { [OutputField(Label = "Article body")] public RichTextValue? Body { get; set; } } // On startup, register the assembly containing RichTextOutputBinding: var binder = new MetadataBinder(); binder.RegisterAssembly(typeof(RichTextOutputBinding).Assembly); // Output metadata produced: // { "id": "Body", "type": "rich-text", "customProperties": { "toolbar": "full", "allowImages": true } } ``` -------------------------------- ### Annotating Request/Response Properties with [InputField] and [OutputField] Source: https://context7.com/unops/uimetadataframework/llms.txt Use [InputField] and [OutputField] attributes to control form property labels, visibility, ordering, and required status. Properties without these attributes are included in metadata by default but can be excluded with [NotField]. ```csharp using UiMetadataFramework.Core.Binding; public class SearchUsers { public class Request : IRequest { // Visible text input, required before submit [InputField(Label = "Name filter", Required = false, OrderIndex = 0)] public string? NameFilter { get; set; } // Hidden input — still sent to server but not shown in the UI [InputField(Hidden = true, OrderIndex = 99)] public int? TenantId { get; set; } } public class Response : FormResponse { // Shown as a "text" output component [OutputField(Label = "Total users found", OrderIndex = 0)] public int TotalCount { get; set; } // Hidden output — available in response payload but not displayed [OutputField(Hidden = true)] public string? DebugInfo { get; set; } } } ``` -------------------------------- ### Displaying Paginated Data with Paginator Source: https://context7.com/unops/uimetadataframework/llms.txt Defines a form for listing users with filtering and pagination. The `Paginator` input controls the `PaginatedData` output. Ensure `PaginatedData` attribute matches the `Paginator` field name. ```csharp using UiMetadataFramework.Basic.Inputs.Paginator; using UiMetadataFramework.Basic.Output.PaginatedData; using UiMetadataFramework.Core.Binding; [Form(Label = "User List", PostOnLoad = true)] public class ListUsers : Form { public class Request : IRequest { [InputField(Label = "Name filter")] public string? Name { get; set; } // Paginator is rendered as a hidden composite input; links to "Results" below [InputField] public Paginator? Paging { get; set; } } public class Response : FormResponse { // "Paging" refers to the name of the Paginator input field above [OutputField] [PaginatedData("Paging")] public PaginatedData? Results { get; set; } } public class UserRow { [OutputField(Label = "ID")] public int Id { get; set; } [OutputField(Label = "Name")] public string Name { get; set; } = null!; } protected override Response Handle(Request request) { var page = request.Paging?.PageIndex ?? 0; var size = request.Paging?.PageSize ?? 20; // Simulate data retrieval var allUsers = Enumerable.Range(1, 100) .Select(i => new UserRow { Id = i, Name = $ ``` ```csharp "User " + i }) .Where(u => request.Name == null || u.Name.Contains(request.Name)) .ToList(); return new Response { Results = new PaginatedData { Results = allUsers.Skip(page * size).Take(size), TotalCount = allUsers.Count } }; } } ``` -------------------------------- ### RedirectResponse for Navigation Source: https://context7.com/unops/uimetadataframework/llms.txt Use RedirectResponse to navigate the client to a different form after submission. Specify the target form and any input field values to pre-populate. ```csharp using UiMetadataFramework.Basic.Response; using UiMetadataFramework.Basic.Server; // 2. RedirectResponse — navigate client to a different form [Form(Label = "Create User")] public class CreateUser : Form { public class Request : IRequest { [InputField(Label = "Name", Required = true)] public string Name { get; set; } = null!; } protected override RedirectResponse Handle(Request request) { int newId = SaveUser(request.Name); return new RedirectResponse { Form = "my-app/edit-user", InputFieldValues = new Dictionary { ["UserId"] = newId } }; } } ``` -------------------------------- ### ReloadResponse for Reinitialization Source: https://context7.com/unops/uimetadataframework/llms.txt Use ReloadResponse to force the client to re-fetch all metadata and reinitialize the application. This is useful for actions that affect global state, like language changes. ```csharp using UiMetadataFramework.Basic.Response; using UiMetadataFramework.Basic.Server; // 3. ReloadResponse — force client to re-fetch all metadata and reinitialize [Form(Label = "Switch Language")] public class SwitchLanguage : Form { public class Request : IRequest { [InputField(Label = "Language code")] public string Code { get; set; } = null!; } protected override ReloadResponse Handle(Request request) { SetUserLanguage(request.Code); return new ReloadResponse(); // client reloads from scratch } } ``` -------------------------------- ### Executing Forms with FormRunner and MediatR Source: https://context7.com/unops/uimetadataframework/llms.txt FormRunner executes forms by ID, deserializing requests and dispatching them via MediatR. It supports both dynamic and strongly-typed form execution. ```csharp using MediatR; using UiMetadataFramework.Basic.Server; using UiMetadataFramework.Core.Binding; using Microsoft.Extensions.DependencyInjection; // Setup (typically done in DI composition root) var services = new ServiceCollection(); services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly())); services.AddSingleton(sp => { var b = new MetadataBinder(sp); b.RegisterAssembly(typeof(UiMetadataFramework.Basic.Properties).Assembly); b.RegisterAssembly(Assembly.GetExecutingAssembly()); return b; }); services.AddSingleton(sp => { var r = new FormRegister(sp.GetRequiredService()); r.RegisterAssembly(Assembly.GetExecutingAssembly()); return r; }); services.AddTransient(); var provider = services.BuildServiceProvider(); // Execute a form by ID with a dynamic request payload var runner = provider.GetRequiredService(); var result = await runner.RunForm( form: "my-app/add-numbers", request: new { Number1 = 3, Number2 = 5 }, cancellationToken: CancellationToken.None); // result is a boxed AddNumbers.Response { Result = 8 } dynamic response = result; Console.WriteLine(response.Result); // 8 // Or run by strongly-typed form class var result2 = await runner.Run( request: new { Number1 = 10, Number2 = 20 }, cancellationToken: CancellationToken.None); ``` -------------------------------- ### BindToOutput for Chained Form Interactions Source: https://context7.com/unops/uimetadataframework/llms.txt Use BindToOutputAttribute to automatically copy a value from a named output field into an input field after the form response is processed. This enables chaining form submissions. ```csharp using UiMetadataFramework.Basic.EventHandlers; using UiMetadataFramework.Core.Binding; // Scenario: A search form returns an ID as a hidden output field, // and a subsequent "edit" input field should be auto-filled with that ID. public class SearchAndEditRequest : IRequest { [InputField(Label = "Search query")] public string? Query { get; set; } // After response is handled, this input will be set to the value of the "SelectedId" output field. [InputField(Hidden = true)] [BindToOutput("SelectedId")] public int? EditTargetId { get; set; } } public class SearchAndEditResponse : FormResponse { [OutputField(Label = "Name")] public string? Name { get; set; } // Hidden output — its value gets bound to EditTargetId input via [BindToOutput] [OutputField(Hidden = true)] public int? SelectedId { get; set; } } // Resulting event handler in the input field metadata: // { // "id": "bind-to-output", // "runAt": "response:handled", // "customProperties": { "OutputFieldId": "SelectedId" } // } ``` -------------------------------- ### MessageResponse for User Feedback Source: https://context7.com/unops/uimetadataframework/llms.txt Use MessageResponse to display a notification message to the user after a form submission. Ensure the handler is set to 'message' on the client. ```csharp using UiMetadataFramework.Basic.Response; using UiMetadataFramework.Basic.Server; // 1. MessageResponse — display a notification message to the user [Form(Label = "Delete User")] public class DeleteUser : Form { public class Request : IRequest { [InputField(Hidden = true)] public int UserId { get; set; } } protected override MessageResponse Handle(Request request) { // ... perform deletion ... return new MessageResponse { Message = $"User {request.UserId} deleted successfully." }; // Client receives: { "metadata": { "handler": "message" }, "Message": "User 42 deleted..." } } } ``` -------------------------------- ### Generated UI Metadata JSON Source: https://github.com/unops/uimetadataframework/blob/develop/README.md This JSON object represents the metadata generated by UIMF for a form. It can be sent to the frontend to dynamically create the UI. ```json { "customProperties":null, "id":"UiMetadataFramework.Web.Forms.AddNumbers", "inputFields":[ { "processors":[], "required":true, "id":"Number1", "label":"First number", "type":"number", "hidden":false, "orderIndex":0 }, { "processors":[], "required":true, "id":"Number2", "label":"Second number", "type":"number", "hidden":false, "orderIndex":0 } ], "label":"Add 2 numbers", "outputFields":[ { "id":"Result", "label":"Result of your calculation", "type":"number", "hidden":false, "orderIndex":0 } ], "postOnLoad":false, "postOnLoadValidation":true } ``` -------------------------------- ### Embed Forms in Responses with C# InlineForm Source: https://context7.com/unops/uimetadataframework/llms.txt Use the InlineForm output component to render independent nested forms within a response. This is useful for displaying related data sections like orders or settings within a main form. ```csharp using UiMetadataFramework.Basic.Output.InlineForm; using UiMetadataFramework.Core.Binding; [Form(Label = "User Dashboard", PostOnLoad = true)] public class UserDashboard : Form { public class Request : IRequest { [InputField(Hidden = true)] public int UserId { get; set; } } public class Response : FormResponse { [OutputField(Label = "Recent Orders")] public InlineForm? OrdersSection { get; set; } [OutputField(Label = "User Settings")] public InlineForm? SettingsSection { get; set; } } protected override Response Handle(Request request) { return new Response { OrdersSection = new InlineForm { Form = "my-app/list-orders", InputFieldValues = new Dictionary { ["UserId"] = request.UserId } }, SettingsSection = new InlineForm { Form = "my-app/user-settings", InputFieldValues = new Dictionary { ["UserId"] = request.UserId } } }; } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.