The Razor UI Controls are utilized in new Razor project templates and the World Validation Application.
UI Component List
Currently the component libraries include common Bootstrap UI Form Controls and Navigation Components:
Control | Description |
---|---|
@Html.ValidationSummary | Show validation summary error message unless there's an error in specified fields |
@Html.ValidationSuccess | Display a "Success Alert Box" |
@Html.FormInput | Display a <input type="text"/> UI Control |
@Html.FormTextarea | Display a <textarea/> UI Control |
@Html.FormSelect | Display a <select/> UI Control |
@Html.FormInput | Display a <input type="checkbox"/> UI Control |
@Html.HiddenInputs | Emit HTML <input type="hidden"/> field for each specified Key/Value pair entry |
@Html.SvgImage | Return <svg/> markup for the named image |
@Html.Nav | Display a list of NavItem's |
@Html.Navbar | Display the navbar main menu |
@Html.NavLink | Display a nav-link nav-item |
@Html.NavButtonGroup | Display a list of NavItem's btn-group |
Bootstrap UI Form Controls
The Bootstrap UI form controls include built-in support for validation where they can render validation errors from ServiceStack's
ResponseStatus
object, e.g the Login Page in World Validation:
<form action="/auth/credentials" method="post" class="col-lg-4">
<div class="form-group">
@Html.ValidationSummary(new[]{ "userName","password" },
new { @class = "alert alert-warning" })
@Html.HiddenInputs(new {
@continue = Html.Query("continue") ?? "/server-razor/",
errorView = "/server-razor/login"
})
</div>
<div class="form-group">
@Html.FormInput(new { id = "userName" }, new InputOptions {
Label = "Email",
Help = "Email you signed up with",
Size = "lg",
})
</div>
<div class="form-group">
@Html.FormInput(new { id = "password", type = "password" }, new InputOptions {
Label = "Password",
Help = "6 characters or more",
Size = "lg",
PreserveValue = false,
})
</div>
<div class="form-group">
@Html.FormInput(new {
id = "rememberMe",
type = "checkbox",
@checked = true,
},
new InputOptions { Label = "Remember Me" })
</div>
<div class="form-group">
<button type="submit" class="btn btn-lg btn-primary">Login</button>
</div>
<div class="form-group">
<a class="lnk" href="/server-razor/register">Register New User</a>
</div>
</form>
Login Page UI
The Login Page contains a standard Bootstrap Username/Password form with labels, placeholders and help text, which initially looks like:
What it looks like after submitting an empty form with Server Exception Errors rendered against their respective fields:
Form Control Properties
The Razor controls uses anonymous objects and camelCase properties for its unbounded HTML Element Attribute List
for attributes you want to add to the underlying HTML <input/>
Element and a Typed InputOptions
Class to specify the controls other
high-level features, typically like:
@Html.ControlName(new { /*htmlAttrs*/ }, new InputOptions { ... })
The typed InputOptions
class supports the following features:
/// High-level Input options for rendering HTML Input controls
public class InputOptions
{
/// Display the Control inline
public bool Inline { get; set; }
/// Label for the control
public string Label { get; set; }
/// Class for Label
public string LabelClass { get; set; }
/// Override the class on the error message (default: invalid-feedback)
public string ErrorClass { get; set; }
/// Small Help Text displayed with the control
public string Help { get; set; }
/// Bootstrap Size of the Control: sm, lg
public string Size { get; set; }
/// Multiple Value Data Source for Checkboxes, Radio boxes and Select Controls
public object Values { get; set; }
/// Typed setter of Multi Input Values
public IEnumerable<KeyValuePair<string, string>> InputValues
{
set => Values = value;
}
/// Whether to preserve value state after post back
public bool PreserveValue { get; set; } = true;
/// Whether to show Error Message associated with this control
public bool ShowErrors { get; set; } = true;
}
Contacts Page
The Contacts Page shows a more complete example with a number of different UI Controls.
<form action="/contacts" method="post" class="col-lg-4">
<div class="form-group">
@Html.ValidationSummary(new[]{ "title","name","color","age","filmGenres","agree" })
@Html.HiddenInputs(new { @continue = Continue, errorView = Continue })
</div>
<div class="form-group">
@Html.FormInput(new {
id = "title",
type = "radio",
}, new InputOptions {
Values = Html.ContactTitles(),
Inline = true,
})
</div>
<div class="form-group">
@Html.FormInput(new {
id = "name",
placeholder = "Name",
}, new InputOptions {
Label = "Full Name",
Help = "Your first and last name",
})
</div>
<div class="form-group">
@Html.FormSelect(new {
id = "color",
@class = "col-4",
}, new InputOptions {
Label = "Favorite color",
Values = new StringDictionary { {"",""} }.Merge(Html.ContactColors()),
})
</div>
<div class="form-group">
@Html.FormInput(new {
id = "filmGenres",
type = "checkbox",
}, new InputOptions {
Label = "Favorite Film Genres",
Help = "choose one or more",
Values = Html.ContactGenres()
})
</div>
<div class="form-group">
@Html.FormInput(new {
id = "age",
type = "number",
min = 13,
placeholder = "Age",
@class = "col-3",
})
</div>
<div class="form-group">
@Html.FormInput(new {
id = "agree",
type = "checkbox",
},
new InputOptions { Label = "Agree to terms and conditions" })
</div>
<div class="form-group">
<button class="btn btn-primary" type="submit">Add Contact</button>
<a href="/server-razor/contacts/">reset</a>
</div>
</form>
Both Server UI Controls provide auto Validation Form Binding for any validation rules specified on the CreateContact
Validator:
public class CreateContactValidator : AbstractValidator<CreateContact>
{
public CreateContactValidator()
{
RuleFor(r => r.Title).NotEqual(Title.Unspecified).WithMessage("Please choose a title");
RuleFor(r => r.Name).NotEmpty();
RuleFor(r => r.Color).Must(x => x.IsValidColor()).WithMessage("Must be a valid color");
RuleFor(r => r.FilmGenres).NotEmpty().WithMessage("Please select at least 1 genre");
RuleFor(r => r.Age).GreaterThan(13).WithMessage("Contacts must be older than 13");
RuleFor(x => x.Agree).Equal(true).WithMessage("You must agree before submitting");
}
}
As well as any ArgumentException
thrown within the Service Implementation:
public object Any(CreateContact request)
{
var newContact = request.ConvertTo<Data.Contact>();
newContact.Id = Interlocked.Increment(ref Counter);
newContact.UserAuthId = this.GetUserId();
newContact.CreatedDate = newContact.ModifiedDate = DateTime.UtcNow;
var contacts = Contacts.Values.ToList();
var alreadyExists = contacts.Any(x => x.UserAuthId == newContact.UserAuthId && x.Name == request.Name);
if (alreadyExists)
throw new ArgumentException($"You already have contact named '{request.Name}'",nameof(request.Name));
Contacts[newContact.Id] = newContact;
return new CreateContactResponse { Result = newContact.ConvertTo<Contact>() };
}
Contacts Page UI
The Contacts Page is representative of a more complex page that utilizes a variety of different form controls where the same page is also responsible for rendering the list of existing contacts:
Here's an example of what a partially submitted invalid form looks like:
To view the complete implementation in context checkout World Validation Server Implementation.
Navigation Controls
The Server Navigation Controls are used to render your Apps Unified Navigation
where you can use the @Html.Navbar()
and @Html.NavButtonGroup()
methods to render NavItems:
Navbar
You can render the main menu navigation using the Navbar HTML Helper:
@Html.Navbar()
Which by default renders the View.NavItems
main navigation, using the default NavOptions
and User Attributes (if authenticated):
You can also render a different Navigation List with:
@Html.Navbar(Html.GetNavItems("submenu"))
Which can be customized using the different NavOptions
properties above, in camelCase:
@Html.Navbar(Html.GetNavItems("submenu"), new NavOptions {
NavClass = "navbar-nav navbar-light bg-light"
})
Button group
The NavButtonGroup
HTML Helper can render NavItems in a button group, e.g. the
OAuth buttons
are rendered with:
@Html.NavButtonGroup(Html.GetNavItems("auth"), new NavOptions {
NavClass = "",
NavItemClass = "btn btn-block btn-lg",
})
Which renders a vertical, spaced list of buttons which look like:
Razor Pages
The same server controls are available in ServiceStack.Razor Apps as HTML Helper extension methods:
Navbar
@Html.Navbar()
@Html.Navbar(Html.GetNavItems("submenu"))
@Html.Navbar(Html.GetNavItems("submenu"), new NavOptions {
NavClass = "navbar-nav navbar-light bg-light"
})
NavButtonGroup
@Html.NavButtonGroup(Html.GetNavItems("auth"), new NavOptions {
NavClass = "",
NavItemClass = "btn btn-block btn-lg",
})
NavOptions Properties
Each Nav UI Control can be further customized by overriding the properties on the typed NavOptions
class:
public class NavOptions
{
/// User Attributes for conditional rendering, e.g:
/// - auth - User is Authenticated
/// - role:name - User Role
/// - perm:name - User Permission
public HashSet<string> Attributes { get; set; }
/// Path Info that should set as active
public string ActivePath { get; set; }
/// Prefix to include before NavItem.Path (if any)
public string BaseHref { get; set; }
// Custom classes applied to different navigation elements (defaults to Bootstrap classes)
public string NavClass { get; set; }
public string NavItemClass { get; set; }
public string NavLinkClass { get; set; }
public string ChildNavItemClass { get; set; }
public string ChildNavLinkClass { get; set; }
public string ChildNavMenuClass { get; set; }
public string ChildNavMenuItemClass { get; set; }
}