9.1 KiB
sidebar_position | title |
---|---|
5 | Creation of a confirmation popup |
Creation of a confirmation popup
In order to create our popup, create the Modals
folder at the root of the site.
Create the razor component DeleteConfirmation.razor
and its class DeleteConfirmation.razor.cs
.
<div class="simple-form">
<p>
Are you sure you want to delete @item.DisplayName ?
</p>
<button @onclick="ConfirmDelete" class="btn btn-primary">Delete</button>
<button @onclick="Cancel" class="btn btn-secondary">Cancel</button>
</div>
public partial class DeleteConfirmation
{
[CascadingParameter]
public BlazoredModalInstance ModalInstance { get; set; }
[Inject]
public IDataService DataService { get; set; }
[Parameter]
public int Id { get; set; }
private Item item = new Item();
protected override async Task OnInitializedAsync()
{
// Get the item
item = await DataService.GetById(Id);
}
void ConfirmDelete()
{
ModalInstance.CloseAsync(ModalResult.Ok(true));
}
void Cancel()
{
ModalInstance.CancelAsync();
}
}
Open the Pages/List.razor.cs
file and add the changes:
...
[Inject]
public NavigationManager NavigationManager { get; set; }
[CascadingParameter]
public IModalService Modal { get; set; }
...
private async void OnDelete(int id)
{
var parameters = new ModalParameters();
parameters.Add(nameof(Item.Id), id);
var modal = Modal.Show<DeleteConfirmation>("Delete Confirmation", parameters);
var result = await modal.Result;
if (result.Cancelled)
{
return;
}
await DataService.Delete(id);
// Reload the page
NavigationManager.NavigateTo("list", true);
}
Concept: Cascading Parameters
Component CascadingValue
An ancestor component provides cascading value using the CascadingValue
framework Blazor component, which encapsulates a subtree of a component hierarchy and provides a unique value to all components in its subtree.
The following example shows the flow of theme information through the component hierarchy of a layout component to provide a CSS style class to child component buttons.
The following ThemeInfo
C# class is placed in a folder named UIThemeClasses
and specifies theme information.
:::info For the samples in this section, the application namespace is BlazorSample . When experimenting with the code in your own sample application, replace the application namespace with the namespace of your sample application. :::
namespace BlazorSample.UIThemeClasses
{
public class ThemeInfo
{
public string? ButtonClass { get; set; }
}
}
The following layout specifies theme information ( ThemeInfo
) as a cascading value for all components that make up the layout body of the Body
property.
The value ButtonClass
is assigned to btn-success
, which is a style of start button. Any component descending in the component hierarchy can use the ButtonClass
property through the cascading ThemeInfo
value.
@inherits LayoutComponentBase
@using BlazorSample.UIThemeClasses
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<div class="main">
<CascadingValue Value="theme">
<div class="content px-4">
@Body
</div>
</CascadingValue>
</div>
</div>
@code {
private ThemeInfo theme = new() { ButtonClass = "btn-success" };
}
Attribute [CascadingParameter]
To use cascading values, descendant components declare cascading parameters using the [CascadingParameter]
attribute.
Cascading values are related to cascading parameters by type.
The following component binds the cascading ThemeInfo
value to a cascading parameter, optionally using the same ThemeInfo
name. The parameter is used to define the CSS class for the Increment Counter (Themed)
button.
@page "/themed-counter"
@using BlazorSample.UIThemeClasses
<h1>Themed Counter</h1>
<p>Current count: @currentCount</p>
<p>
<button class="btn" @onclick="IncrementCount">
Increment Counter (Unthemed)
</button>
</p>
<p>
<button
class="btn @(ThemeInfo is not null ? ThemeInfo.ButtonClass : string.Empty)"
@onclick="IncrementCount">
Increment Counter (Themed)
</button>
</p>
@code {
private int currentCount = 0;
[CascadingParameter]
protected ThemeInfo? ThemeInfo { get; set; }
private void IncrementCount()
{
currentCount++;
}
}
Cascading Multiple Values
To cascade multiple values of the same type in the same subtree, supply a unique Name
string to each CascadingValue
component and its corresponding [CascadingParameter]
attributes.
In the following example, two CascadingValue
components cascade different instances of CascadingType
:
<CascadingValue Value="@parentCascadeParameter1" Name="CascadeParam1">
<CascadingValue Value="@ParentCascadeParameter2" Name="CascadeParam2">
...
</CascadingValue>
</CascadingValue>
@code {
private CascadingType parentCascadeParameter1;
[Parameter]
public CascadingType ParentCascadeParameter2 { get; set; }
...
}
In a descendant component, cascading parameters receive their cascading values from the ancestor component with the Name
attribute:
...
@code {
[CascadingParameter(Name = "CascadeParam1")]
protected CascadingType ChildCascadeParameter1 { get; set; }
[CascadingParameter(Name = "CascadeParam2")]
protected CascadingType ChildCascadeParameter2 { get; set; }
}
Pass data in a component hierarchy
Cascading parameters also allow components to pass data through a component hierarchy. Consider the following UI tab set example, where a tab set component manages a series of individual tabs.
Create an ITab
interface that tabs implement in a folder named UIInterfaces
.
using Microsoft.AspNetCore.Components;
namespace BlazorSample.UIInterfaces
{
public interface ITab
{
RenderFragment ChildContent { get; }
}
}
The following TabSet
component manages a set of tabs. The components of the Tab
tab set, which are created later in this section, provide the list items ( <li>...</li>
) of the list ( <ul>... </ul>
).
Tab
child components are not explicitly passed as parameters to TabSet
.
Instead, Tab
child components are part of TabSet
child content.
However, the TabSet
still requires a Tab
reference to each component in order to display the headers and the active tab.
To enable this coordination without requiring additional code, the TabSet
component can present itself as a cascading value which is then retrieved by descendant Tab
components.
@using BlazorSample.UIInterfaces
<!-- Display the tab headers -->
<CascadingValue Value=this>
<ul class="nav nav-tabs">
@ChildContent
</ul>
</CascadingValue>
<!-- Display body for only the active tab -->
<div class="nav-tabs-body p-4">
@ActiveTab?.ChildContent
</div>
@code {
[Parameter]
public RenderFragment ChildContent { get; set; }
public ITab ActiveTab { get; private set; }
public void AddTab(ITab tab)
{
if (ActiveTab == null)
{
SetActiveTab(tab);
}
}
public void SetActiveTab(ITab tab)
{
if (ActiveTab != tab)
{
ActiveTab = tab;
StateHasChanged();
}
}
}
Descendant Tab
components capture the TabSet
container as a cascading parameter. The Tab
components add to the TabSet
coordinate and to set the active tab.
@using BlazorSample.UIInterfaces
@implements ITab
<li>
<a @onclick="ActivateTab" class="nav-link @TitleCssClass" role="button">
@Title
</a>
</li>
@code {
[CascadingParameter]
public TabSet ContainerTabSet { get; set; }
[Parameter]
public string Title { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
private string TitleCssClass =>
ContainerTabSet.ActiveTab == this ? "active" : null;
protected override void OnInitialized()
{
ContainerTabSet.AddTab(this);
}
private void ActivateTab()
{
ContainerTabSet.SetActiveTab(this);
}
}
The following ExampleTabSet
component uses the TabSet
component, which contains three Tab
components.
@page "/example-tab-set"
<TabSet>
<Tab Title="First tab">
<h4>Greetings from the first tab!</h4>
<label>
<input type="checkbox" @bind="showThirdTab" />
Toggle third tab
</label>
</Tab>
<Tab Title="Second tab">
<h4>Hello from the second tab!</h4>
</Tab>
@if (showThirdTab)
{
<Tab Title="Third tab">
<h4>Welcome to the disappearing third tab!</h4>
<p>Toggle this tab from the first tab.</p>
</Tab>
}
</TabSet>
@code {
private bool showThirdTab;
}