What are Output Fields?
Output fields represent data returned from the server to the client. They are properties on the response class decorated with OutputFieldAttribute. The framework generates metadata that tells the client how to render the response data.
OutputFieldAttribute
The OutputFieldAttribute decorates properties to mark them as output fields:
/home/daytona/workspace/source/UiMetadataFramework.Core/Binding/Field/OutputFieldAttribute.cs
public class OutputFieldAttribute : FieldAttribute
{
public OutputFieldAttribute () : base ( MetadataBinder . ComponentCategories . Output )
{
}
}
Notice that unlike InputFieldAttribute, there’s no Required property. Output fields simply display what the server returns.
Inherited Properties from FieldAttribute
OutputFieldAttribute inherits all properties from FieldAttribute:
/home/daytona/workspace/source/UiMetadataFramework.Core/Binding/Field/FieldAttribute.cs:23-37
public abstract class FieldAttribute : Attribute
{
public bool Hidden { get ; set ; }
public string ? Label { get ; set ; }
public int OrderIndex { get ; set ; }
}
Field Properties
Label
Human-readable label displayed next to the output value:
public class Response : FormResponse
{
[ OutputField ( Label = "User ID" )]
public int UserId { get ; set ; }
[ OutputField ( Label = "Registration Date" )]
public DateTime RegisteredAt { get ; set ; }
}
If not specified, the property name is used as the label.
OrderIndex
Controls the rendering order. Lower values appear first:
public class Response : FormResponse
{
[ OutputField ( Label = "First name" , OrderIndex = 1 )]
public string ? FirstName { get ; set ; }
[ OutputField ( Label = "Last name" , OrderIndex = 2 )]
public string ? LastName { get ; set ; }
[ OutputField ( Label = "Email" , OrderIndex = 3 )]
public string ? Email { get ; set ; }
}
Use the same OrderIndex values in both request and response classes to maintain consistent field ordering.
Hidden
Hides the field from display. Useful for returning data that clients need but shouldn’t show to users:
public class Response : FormResponse
{
[ OutputField ( Label = "Username" )]
public string ? Username { get ; set ; }
[ OutputField ( Hidden = true )]
public string ? InternalToken { get ; set ; } // Client needs it, but user doesn't see it
}
Type-to-Component Mapping
Just like input fields, output fields are mapped to UI components based on their C# type:
Built-in Mappings
// String/Boolean → Text display
[ OutputField ( Label = "Name" )]
public string ? Name { get ; set ; }
[ OutputField ( Label = "Active" )]
public bool IsActive { get ; set ; }
// DateTime → Formatted date display
[ OutputField ( Label = "Created" )]
public DateTime CreatedAt { get ; set ; }
// Decimal/Double → Formatted number
[ OutputField ( Label = "Price" )]
public decimal Price { get ; set ; }
// Int → Number display
[ OutputField ( Label = "Count" )]
public int Count { get ; set ; }
String/Boolean Output Example
From the source code:
/home/daytona/workspace/source/UiMetadataFramework.Basic/Output/Text/StringOutputComponentBinding.cs
public class StringOutputComponentBinding : ComponentBinding
{
internal const string ControlName = "text" ;
public StringOutputComponentBinding () : base (
MetadataBinder . ComponentCategories . Output ,
serverTypes :
[
typeof ( string ),
typeof ( bool )
],
componentType : "text" ,
metadataFactory : null )
{
}
}
This binding maps both string and bool types to a text output component.
A single component can handle multiple server types. The client decides how to format each type.
Real-World Example
From the test suite:
/home/daytona/workspace/source/UiMetadataFramework.Tests/MediatrTests.cs:54-67
public class Response : FormResponse < FormResponseMetadata >
{
[ OutputField ( Label = "First name" , OrderIndex = 1 )]
public string ? FirstName { get ; set ; }
[ OutputField ( Label = "DoB" , OrderIndex = 2 )]
public DateTime ? DateOfBirth { get ; set ; }
[ OutputField ( Hidden = true )]
public int Height { get ; set ; }
[ OutputField ( Hidden = true )]
public decimal Weight { get ; set ; }
}
This generates metadata with:
2 visible output fields (FirstName, DateOfBirth)
2 hidden output fields (Height, Weight)
All response classes inherit from FormResponse:
public class Response : FormResponse
{
[ OutputField ( Label = "Message" )]
public string ? Message { get ; set ; }
}
The FormResponse class includes response metadata:
/home/daytona/workspace/source/UiMetadataFramework.Core/FormResponseMetadata.cs
public class FormResponseMetadata
{
public string ? Handler { get ; internal set ; }
public IList < ClientFunctionMetadata >? FunctionsToRun { get ; set ; }
}
Response Handlers
The Handler property tells the client how to process the response:
// Default: render the output fields
public class Response : FormResponse
{
// Metadata.Handler = null (uses default "object" handler)
}
// Custom: use a specific response handler
public class MessageResponse : FormResponse
{
public MessageResponse () : base ( "message" )
{
// Metadata.Handler = "message"
}
public string Message { get ; set ; } = null ! ;
}
From the source:
/home/daytona/workspace/source/UiMetadataFramework.Basic/Response/MessageResponse.cs
public class MessageResponse : FormResponse
{
public MessageResponse () : base ( "message" )
{
}
public string Message { get ; set ; } = null ! ;
}
The client must implement a "message" handler that knows how to display messages.
Response handlers allow you to create rich response types like redirects, modals, file downloads, etc.
Common Response Patterns
Simple Object Response
Display output fields in a structured layout:
public class Response : FormResponse
{
[ OutputField ( Label = "User ID" )]
public int Id { get ; set ; }
[ OutputField ( Label = "Full Name" )]
public string ? FullName { get ; set ; }
[ OutputField ( Label = "Email" )]
public string ? Email { get ; set ; }
}
Message Response
Show a simple message to the user:
public class Response : FormResponse
{
public Response () : base ( "message" )
{
}
public string Message { get ; set ; } = null ! ;
}
// Usage in form handler:
protected override Response Handle ( Request request )
{
// Process request...
return new Response
{
Message = "User registered successfully!"
};
}
Redirect Response
Redirect to another form or URL:
public class Response : FormResponse
{
public Response () : base ( "redirect" )
{
}
public string FormId { get ; set ; } = null ! ;
}
// Usage:
return new Response
{
FormId = "user-details"
};
Reload Response
Trigger a form or page reload:
public class Response : FormResponse
{
public Response () : base ( "reload" )
{
}
}
Nullable Output Fields
Output fields often use nullable types since the server may not always have data:
public class Response : FormResponse
{
[ OutputField ( Label = "Name" )]
public string ? Name { get ; set ; } // May be null
[ OutputField ( Label = "Birth Date" )]
public DateTime ? BirthDate { get ; set ; } // May be null
[ OutputField ( Label = "ID" )]
public int Id { get ; set ; } // Always has value
}
The client should handle null values gracefully (e.g., display empty string or placeholder).
Custom Output Components
You can create custom output components for complex types:
public class ChartBinding : ComponentBinding
{
public ChartBinding () : base (
MetadataBinder . ComponentCategories . Output ,
serverType : typeof ( ChartData ),
componentType : "chart" ,
metadataFactory : typeof ( ChartMetadataFactory ))
{
}
}
public class ChartData
{
public string [] Labels { get ; set ; }
public int [] Values { get ; set ; }
}
// Use in response:
public class Response : FormResponse
{
[ OutputField ( Label = "Sales Chart" )]
public ChartData ? SalesData { get ; set ; }
}
The client’s "chart" component renders the data as a visual chart.
The framework generates the same FieldMetadata structure for output fields:
{
"id" : "FirstName" ,
"label" : "First name" ,
"orderIndex" : 1 ,
"hidden" : false ,
"component" : {
"type" : "text" ,
"serverType" : "System.String"
}
}
Response Functions
You can trigger client-side functions when the response is received:
protected override Response Handle ( Request request )
{
var response = new Response
{
Message = "Success!"
};
response . Metadata . FunctionsToRun = new List < ClientFunctionMetadata >
{
new ClientFunctionMetadata
{
Id = "show-notification" ,
Parameters = new { Text = "Operation completed" }
}
};
return response ;
}
The client will run the "show-notification" function before handling the response.
Use response functions for side effects like showing toasts, playing sounds, or triggering analytics.
Best Practices
Use Nullable Types Make output fields nullable when data may not be available
Consistent Ordering Match OrderIndex between input and output for predictable layouts
Descriptive Labels Use clear labels that make sense to end users
Hide Sensitive Data Use Hidden = true for data clients need but users shouldn’t see
Next Steps
Input Fields Learn about input fields and validation
Forms Understand the complete form structure
Metadata Binding See how output fields are converted to metadata
Creating Forms Learn how to build complete forms with outputs