Branch | Status |
---|---|
master | |
dev | |
There are 2 Packages available on Nuget.org
This Package includes support for basic authentication in Blazor.
- User Controller for basic Authentication
- UserStateProvider for use in Components
- Components for Login & Register
- Basic User Service to test Authentication with Users stored in Memory
This Project contains various Components for Blazor. All Components can be used on Client-side or Server-side blazor.
Included Components
- AutoTable - Automatically generates a Table using reflection
- BlazorTable - Generates a table based on Template Parameters
- Expandable Container - Supports Animated Expansion/Collapse of Content
- Gallery - Gallery Component with Data Source
- Nav Bar - Horizontal Navigation
- Nav Menu - Vertical Navigation
- UIList - List Component with Data Source
- UITree - Tree Component with various Eventhandlers and Data Source
- Progressbar - Spinner and various Progressbars
Download the NuGet Package BlazingComponents.Authentication
Namespaces for Imports:
- BlazingComponents.Authentication.Areas.Components
- BlazingComponents.Authentication.Services
- BlazingComponents.Authentication.Models
- BlazingComponents.Authentication.Interfaces
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddAuthentication("BasicAuthentication")
.AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null)
.AddCookie();
services.AddAuthorization();
services.AddControllers()
.AddApplicationPart(typeof(UserController).GetTypeInfo().Assembly)
.AddControllersAsServices();
services.AddSingleton<IUserService, BasicUserService>();
services.AddScoped(s =>
{
// Creating the URI helper needs to wait until the JS Runtime is initialized, so defer it.
var uriHelper = s.GetRequiredService<IUriHelper>();
return new HttpClient
{
BaseAddress = new Uri(uriHelper.GetBaseUri())
};
});
//...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//...
app.UseAuthentication();
app.UseAuthorization();
//...
}
<UserStateProvider>
<Router AppAssembly="typeof(Startup).Assembly" />
</UserStateProvider>
public class UserState
{
public string Id { get; set; }
public string DisplayName { get; set; }
public bool IsLoggedIn { get; set; }
public EUserRole Role { get; set; }
public IDictionary<string, object> UserData { get; set; }
}
The User Controller will bind to /user, /login, and /register. The UserStateProvider will call these Controller-functions to perform logged-in state-check, login and registration. When replacing the User Controller, make sure the URL stays the same and return a UserState object as it is currently hard coded into the UserStateProvider.
[HttpGet("user")]
public ActionResult<UserState> GetUser();
[HttpGet("login")]
public async Task<ActionResult<UserState>> Login();
[HttpPost("register")]
public async Task<ActionResult<UserState>> Register(UserCredentials userCredentials);
[HttpPut("logout")]
public async Task<ActionResult<UserState>> SignOut();
Feel free to replace the BasicUserService with your own implementation of IUserService.
public interface IUserService
{
Task<UserState> RegisterAsync(UserCredentials credentials);
Task<UserState> LoginAsync(UserCredentials credentials);
}
The BasicAuthenticationHandler receives a instance of IUserService via Dependency Injection.
Download the NuGet Package BlazingComponents.Lib
Namespaces for Imports:
- BlazingComponents.Lib.Areas.Components
- BlazingComponents.Lib.Services
- BlazingComponents.Lib.Models
Your _Host.cshtml should look somewhat like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Your Title Here</title>
<base href="~/" />
<link href="_content/BlazingComponents.Lib/libs/bootstrap/css/bootstrap.min.css" rel="stylesheet" />
<link href="_content/BlazingComponents.Lib/libs/fontawesome/css/all.min.css" rel="stylesheet" />
<link href="_content/BlazingComponents.Lib/css/toast.css" rel="stylesheet">
<link href="_content/BlazingComponents.Lib/css/site.css" rel="stylesheet" />
</head>
<body>
<app>@(await Html.RenderComponentAsync<App>())</app>
<script src="_framework/blazor.server.js"></script>
<script type="text/javascript" src="_content/BlazingComponents.Lib/libs/jquery/jquery.min.js"></script>
<script type="text/javascript" src="_content/BlazingComponents.Lib/libs/bootstrap/js/bootstrap.min.js"></script>
</body>
</html>
<Router AppAssembly="typeof(Startup).Assembly" />
<ToastContainer />
Edit your Startup.cs like this:
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddComponentLib();
services.AddScoped(s =>
{
// Creating the URI helper needs to wait until the JS Runtime is initialized, so defer it.
var uriHelper = s.GetRequiredService<IUriHelper>();
return new HttpClient
{
BaseAddress = new Uri(uriHelper.GetBaseUri())
};
});
//...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//...
app.UseAuthentication();
app.UseAuthorization();
//...
}
Name | Type | Description | |
---|---|---|---|
ItemKeyDelegate | Func<T, object> | Delegate to pass to the @key directive for each item in a row | |
ExpandedItemKeyDelegate | Func<T, object> | Delegate to pass to the @key directive for each item in the expanded content of a Row | |
TableClass | string | CSS-class for the table element | |
SelectedClass | string | CSS-class for the row when it is in the selected state | |
Properties | IEnumerable | List the properties to use for column-generation (default: all properties are used) | |
ExpandedRowTemplate | RenderFragment | Render Fragment for table expanded row content | |
Items | IReadOnlyList | Items to be used for table-row generation | |
Selectable | bool | Determines whether rows should be selectable | |
Expandable | bool | Determines whether rows should be expandable | |
MultiSelect | bool | Determines whether multiple rows should be selectable | |
MultiExpand | bool | Determines whether multiple rows should be expandable | |
SelectedItems | IList | The selected items | |
ExpandedItems | IList | The expanded items | |
OnSelect | EventCallback | emitted when a row is selected | |
OnExpand | EventCallback | emitted when a row is expanded | |
OnSelectMany | EventCallback<IEnumerable> | emitted when a row or multiple rows are selected | |
OnExpandMany | EventCallback<IEnumerable> | emitted when a row or multiple rows is expanded |
<AutoTable Items="Persons" Selectable="true" MultiSelect="false">
<ExpandedRowTemplate Context="person">
<td colspan="6">
@($"{person?.FirstName} {person.LastName} born on {person?.BirthDate}")
</td>
</ExpandedRowTemplate>
</AutoTable>
Name | Type | Description | |
---|---|---|---|
ItemKeyDelegate | Func<T, object> | Delegate to pass to the @key directive for each item in a row | |
ExpandedItemKeyDelegate | Func<T, object> | Delegate to pass to the @key directive for each item in the expanded content of a Row | |
TableClass | string | CSS-class for the table element | |
SelectedClass | string | CSS-class for the row when it is in the selected state | |
TableHeader | RenderFragment | Render Fragment for table header | |
RowTemplate | RenderFragment | Render Fragment for table row content | |
ExpandedRowTemplate | RenderFragment | Render Fragment for table expanded row content | |
TableFooter | RenderFragment | Render Fragment for table footer | |
Items | IReadOnlyList | Items to be used for table-row generation | |
Selectable | bool | Determines whether rows should be selectable | |
Expandable | bool | Determines whether rows should be expandable | |
MultiSelect | bool | Determines whether multiple rows should be selectable | |
MultiExpand | bool | Determines whether multiple rows should be expandable | |
UsePagination | bool | Determines whether the table should have pagination | |
ItemsPerPageOptions | bool | The available options for items per page in the pagination | |
SelectedItems | IList | The selected items | |
ExpandedItems | IList | The expanded items | |
OnSelect | EventCallback | emitted when a row is selected | |
OnExpand | EventCallback | emitted when a row is expanded | |
OnSelectMany | EventCallback<IEnumerable> | emitted when a row or multiple rows are selected | |
OnExpandMany | EventCallback<IEnumerable> | emitted when a row or multiple rows is expanded | |
<BlazorTable @ref="_tableComponent" Items="Persons" UsePagination="true" Expandable="true" Selectable="true" MultiSelect="true" MultiExpand="false">
<TableHeader>
<th></th>
<th>ID</th>
<th>FirstName</th>
<th>LastName</th>
<th>BirthDate</th>
</TableHeader>
<RowTemplate Context="person">
<td>@person.Id</td>
<td>@person.FirstName</td>
<td>@person.LastName</td>
<td>@person.BirthDate</td>
</RowTemplate>
<ExpandedRowTemplate Context="person">
<td colspan="6">
<table class="table">
<thead>
<tr>
<th>ToString</th>
</tr>
</thead>
<tbody>
<tr>
<td>
@person
</td>
</tr>
</tbody>
</table>
</td>
</ExpandedRowTemplate>
</BlazorTable>
public class GalleryItem<T>
{
public string ImageSource { get; set; }
public T Data { get; set; }
public string Title { get; set; }
public string Subtitle { get; set; }
}
Name | Type | Description | |
---|---|---|---|
Items | List<GalleryItem> | Gallery-Items to be used for the gallery | |
OnSelect | EventCallback | emitted when a gallery-item is selected | |
<BlazorGallery @ref="_gallery" Items="GalleryItems" />
public GalleryItem<string> CurrentItem { get; set; }
public List<GalleryItem<string>> GalleryItems { get; set; } = new List<GalleryItem<string>>
{
new GalleryItem<string> { Data = "Hello I am item 1", ImageSource = "http://wowslider.com/sliders/demo-93/data1/images/sunset.jpg", Subtitle = "Subtile of Item 1", Title = "Item 1"},
new GalleryItem<string> { Data = "Hello I am item 2", ImageSource = "http://wowslider.com/sliders/demo-93/data1/images/sunset.jpg", Subtitle = "Subtile of Item 2", Title = "Item 2"},
new GalleryItem<string> { Data = "Hello I am item 3", ImageSource = "http://wowslider.com/sliders/demo-93/data1/images/sunset.jpg", Subtitle = "Subtile of Item 3", Title = "Item 3"}
};
Name | Type | Description | |
---|---|---|---|
ItemKeyDelegate | Func<T, object> | Delegate to pass to the @key directive for each item in the list | |
ExpandedItemKeyDelegate | Func<T, object> | Delegate to pass to the @key directive for each item in the expanded content of a List Entry | |
CssClass | string | CSS-class for the list (default: "list-group") | |
ItemCssClass | string | CSS-class for the list items (default: "list-group") | |
SelectedCssClass | string | CSS-class for the items when they are in the selected state (default: "list-group-item active") | |
ItemTemplate | RenderFragment | Render Fragment for list item content | |
ExpandedItemTemplate | RenderFragment | Render Fragment for expanded list item content | |
Items | IReadOnlyList | Items to be used for table-row generation | |
Selectable | bool | Determines whether items should be selectable | |
Expandable | bool | Determines whether items should be expandable | |
MultiSelect | bool | Determines whether multiple items should be selectable | |
MultiExpand | bool | Determines whether multiple items should be expandable | |
SelectedItems | IList | The selected items | |
ExpandedItems | IList | The expanded items | |
OnSelect | EventCallback | emitted when a item is selected | |
OnExpand | EventCallback | emitted when a item is expanded | |
OnSelectMany | EventCallback<IEnumerable> | emitted when a item or multiple items are selected | |
OnExpandMany | EventCallback<IEnumerable> | emitted when a item or multiple items is expanded | |
<BlazorList @ref="_listRef" Items="@ListItems" Selectable="true" MultiSelect="false" Expandable="true" MultiExpand="false">
<ItemTemplate Context="item">
<span>@item.FirstName @item.LastName , @item.BirthDate</span>
<button class="btn btn-outline-dark" @onclick="@( () => ExpandItem(item))">Expand</button>
</ItemTemplate>
<ExpandedItemTemplate Context="item">
<ul class="list-group bg-dark text-white">
<li class="list-group-item list-group-item-dark text-white bg-dark">
<span>@item.ToString()</span>
</li>
</ul>
</ExpandedItemTemplate>
</BlazorList>
public class BlazorTreeNode<T>
{
public int Id { get; set; }
public T Data { get; set; }
public BlazorTreeNode<T> Parent { get; set; }
public List<BlazorTreeNode<T>> Children { get; set; } = new List<BlazorTreeNode<T>>();
public int Deep { get; set; }
public string Text { get; set; }
public bool IsExpanded { get; set; } = false;
public bool IsSelected { get; set; } = false;
public bool ChildrenLoaded { get; set; } = false;
public bool IsVisible { get; set; } = true;
}
Name | Type | Description | |
---|---|---|---|
NodeKeyDelegate | Func<BlazorTreeNode, object> | Delegate to pass to the @key directive for each node | |
LazyLoadNodesAsyncDelegate | Func<int?, Task<List<BlazorTreeNode>>> | Delegate for lazy loading nodes asynchronously on init | |
Nodes | List<BlazorTreeNode> | List of nodes | |
OnSelect | EventCallback<BlazorTreeNode> | emitted when a node is selected | |
OnExpand | EventCallback<BlazorTreeNode> | emitted when a node is expanded | |
OnCollapse | EventCallback<BlazorTreeNode> | emitted when a node is collapsed | |
<BlazorTree @ref="_treeRef" Nodes="@_nodes">
</BlazorTree>
public List<BlazorTreeNode<string>> _nodes = new List<BlazorTreeNode<string>>(){
new BlazorTreeNode<string>()
{
Text = "Node 1", Children = new List<BlazorTreeNode<string>>()
{
new BlazorTreeNode<string>() { Text = "ChildNode 1"},
new BlazorTreeNode<string>() { Text = "ChildNode 2"},
new BlazorTreeNode<string>() { Text = "ChildNode 3"},
new BlazorTreeNode<string>() {
Text = "ChildNode 4", Children = new List<BlazorTreeNode<string>>()
{
new BlazorTreeNode<string>() { Text = "Nested ChildNode 1"},
new BlazorTreeNode<string>() { Text = "Nested ChildNode 2"},
new BlazorTreeNode<string>() { Text = "Nested ChildNode 3"},
}
},
}
}
};
public enum EProgressType
{
Spinner,
Progress,
Animated
}
Name | Type | Description | |
---|---|---|---|
ProgressType | EProgressType | Progress Type (Spinner/Bar) | |
ProgressValue | int | Progress percentage between 0 and 100 |
<BlazorProgress ProgressType="@(EProgressType.Animated)" ProgressValue="@Progress"></BlazorProgress>
protected async Task ProgressForward()
{
for (int i = 0; i <= 100; i += 10)
{
Progress = i;
await Task.Delay(300);
StateHasChanged();
}
}
public class NavLinkItem
{
public string DisplayName { get; set; } = "NavLink";
public string Href { get; set; } = "";
public string Icon { get; set; } = "fas fa-link"; // https://fontawesome.com/icons?d=gallery
public NavLinkMatch Match { get; set; } = NavLinkMatch.All;
}
Name | Type | Description | |
---|---|---|---|
Title | string | Title | |
NavLinks | List | Nav-Links to be displayed |
<NavBar Title="Blazing Components" NavLinks="NavLinks" />
Name | Type | Description | |
---|---|---|---|
NavLinks | List | Nav-Links to be displayed |
<NavMenu NavLinks="NavLinks" />
Name | Type | Description | |
---|---|---|---|
AnimateExpandClass | string | css class for expansion | |
AnimateCollapseClass | string | css class for collapse | |
Expanded | bool | Expanded-state | |
CollapsedContent | RenderFragment | RenderFragment for content when container is collapsed | |
ExpandedContent | RenderFragment | RenderFragment for content when container is expanded |
<ExpandableContainer @ref="_expandableContainerRef">
<CollapsedContent>
<NavMenu NavLinks="@NavLinksIconOnly" />
</CollapsedContent>
<ExpandedContent>
<NavMenu NavLinks="@NavLinks" />
</ExpandedContent>
</ExpandableContainer>
Toasts are displayed by using the ToastService. Inject the ToastService:
[Inject]
public IToaster Toaster { get; set; }
or @inject Sotsera.Blazor.Toaster.IToaster Toaster
public ShowToastOnSignalRConnectionStateChange(){
SignalRClient.ConnectionStateChanged += (state) =>
{
if (SignalRClient.IsConnected)
{
Toaster.Success("SignalR connection established");
}
else if(state == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Reconnecting)
{
Toaster.Warning("SignalR connection unstable");
}
else
{
Toaster.Error("SignalR disconnected");
}
};
}
BlazingComponents is licensed under MIT license
Components are styled using the awesome Bootstrap 4 Library
BlazingComponents.Authentication uses Bcrypt.Net-Core to secure Passwords
This Project was created in collaboration with Insite GmbH & Reutlingen University (Hochschule Reutlingen), Faculty Informatics - Medien & Kommunikationsinformatik in the lecture "MKI Projekt: Cloud-in-a-Box" Special thanks to Prof. Dr. Hertkorn for his support and making this project possible.
If you find any problems/bugs or notice any bad practices feel free to open a issue.