I recently challenged myself to take one of my side projects, on of my many multi-million dollar ideas, and develop it using some technologies that I am not fully accustomed to.
For the back-end I am going to use Logic Apps, Azure Functions (JavaScript), Cosmos DB and for the front-end I am going to use Blazor.
What is Blazor?
I am glad you asked.
According to the Blazor Docs, Blazor lets you build interactive web UIs using C# instead of JavaScript. Blazor apps are composed of reusable web UI components implemented using C#, HTML, and CSS. Both client and server code is written in C#, allowing you to share code and libraries.
Blazor is a feature of ASP.NET, the popular web development framework that extends the .NET developer platform with tools and libraries for building web apps.
Prior to learning about Blazor, most of the user interfaces I created were done with React. I like React for a variety of reasons, but the biggest reason was React takes a component approach to building an interactive user interface without all the opinions, such as, Angular.
One of the components I had in my React app was a BusyButton
component.
The BusyButton
component allowed you to set some style properties, e.g. Caption
, Icon
and Color
.
When the user clicked the BusyButton
it would disable itself, preventing multiple click and then switch the text letting the user know something was happening
You could also set an OnClickCallback
event callback if you wanted to respond to the click
event of the button yourself, as opposed to it’s default behavior, e.g. a submit
button.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
<button class="btn btn-@Color" type="submit" disabled="@IsBusy" onclick="@OnClickCallback" @onclick:preventDefault="@PreventDefault"><i class="fal fa-@Icon"></i><span>@DisplayCaption</span></button> @code { [Parameter] public string Icon { get; set; } [Parameter] public string Color { get; set; } [Parameter] public bool IsBusy { get; set; } private string DisplayCaption { get { if (IsBusy) return IsBusyCaption; return Caption; } } [Parameter] public string Caption { get; set; } [Parameter] public string IsBusyCaption { get; set; } [Parameter] public EventCallback<MouseEventArgs> OnClickCallback { get; set; } private bool PreventDefault { get { return OnClickCallback.HasDelegate; } } public BusyButton() { Color = "primary"; IsBusy = false; } } |
The initial build out of the component in Blazor was fairly quick, surprisingly quick, the only tricky part was figuring out how to keep events from propagating if an OnClickCallback
was provided.
Eventually I figured it out.
The differences can be seen in a Create and Delete operation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
@page "/organizations/create" @using Teamaloo.WebApp.Components @using Teamaloo.WebApp.Data @inject AppState appState @inject NavigationManager navigationManager @inject OrganizationService organizationService <h1>Add Organization</h1> <EditForm Model="@organizationAddOptions" OnValidSubmit="(async () => await OnValidSubmitAsync())"> <DataAnnotationsValidator /> <ValidationSummary /> <div class="form-group"> <label for="name">Name</label> <InputText class="form-control" @bind-Value="organizationAddOptions.Name" placeholder="The name of the organization..." /> </div> <BusyButton Caption="Save" IsBusyCaption="Saving..." Icon="check" IsBusy="@isBusy" /> <a class="btn btn-link" href="@string.Format("/organizations")">Cancel</a> </EditForm> @code { private bool isBusy = false; private OrganizationAddOptions organizationAddOptions = new OrganizationAddOptions(); private async Task OnValidSubmitAsync() { isBusy = true; var organization = await organizationService.AddAsync(organizationAddOptions); appState.AddOrUpdateOrganization(organization); isBusy = false; navigationManager.NavigateTo($"/organizations/{organization.Id}"); } } |
In my Create operation, I have a local variable called isBusy
which I bind to the IsBusy
property of the BusyButton
component. I do not provide an OnClientCallback
event handler, the default submit
behavior works for this use case.
When the Save button is clicked, the button disables and sets the text, in my case, to Saving...
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
@page "/organizations/{id:guid}/delete" @using Teamaloo.WebApp.Components @using Teamaloo.WebApp.Data @inject AppState appState @inject NavigationManager navigationManager @inject OrganizationService organizationService <h1>Organization</h1> @if (isLoading) { <p>Loading...</p> } else { <form> <div class="form-group"> <label for="name">Name</label> <p class="form-control-plaintext">@organization.Name</p> </div> <BusyButton Caption="Delete" IsBusyCaption="Deleting..." Icon="trash" color="danger" IsBusy="@isBusy" OnClickCallback="@(async () => await OnValidSubmitAsync())" /> <a class="btn btn-link" href="@string.Format("/organizations/{0}", id)">Cancel</a> </form> } @code { [Parameter] public Guid id { get; set; } private bool isBusy = false; private bool isLoading = true; private Organization organization; protected override async Task OnParametersSetAsync() { isLoading = true; organization = await organizationService.GetByIdAsync(id); isLoading = false; } private async Task OnValidSubmitAsync() { await organizationService.DeleteByIdAsync(id); appState.DeleteOrganizationById(id); navigationManager.NavigateTo("/organizations"); } } |
In my Delete operation I need to handle what happens when the BusyButton
is clicked, to do this, I provide a reference to my OnValidateSubmitAsync
method to the OnClientCallback
property.
Like the Create operation, when the Delete button is clicked it disables the button, changes the text, but in this case, instead of using the button’s default behavior it calls my OnValidateSubmitAsync
method.
This is where I had to include the option to set @onclick:preventDefault
to true
or the Delete operation would occur again, this time causing an error because the record no longer existed in the database.
So far, really liking Blazor, very easy to use, and the fact I don’t have to write JavaScript or TypeScript, makes it even more appealing.
Related Links
Discover more from Matt Ruma
Subscribe to get the latest posts sent to your email.
I believe in your Delete OnValidSubmitAsync you want to set your isBusy to true (and then false) like you did with the create.
Additionally, why not just have a
[Parameter(CaptureUnmatchedValues = true)] public Dictionary AdditionalAttributes { get; set; }
and
remove the PreventDefault check
and
then pass in
type="submit"
as you want/deem necessary.Thanks Erich! Those are excellent suggestions! Much appreciated!