You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
357 lines
9.1 KiB
357 lines
9.1 KiB
![]()
2 years ago
|
---
|
||
|
sidebar_position: 5
|
||
|
title: 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`.
|
||
|
|
||
|
```cshtml title="Modals/DeleteConfirmation.razor"
|
||
|
<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>
|
||
|
```
|
||
|
|
||
|
```csharp title="Modals/DeleteConfirmation.razor.cs"
|
||
|
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:
|
||
|
|
||
|
```csharp title="Pages/List.razor.cs"
|
||
|
...
|
||
|
|
||
|
[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.
|
||
|
:::
|
||
|
|
||
|
```csharp title="UIThemeClasses/ThemeInfo.cs"
|
||
|
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.
|
||
|
|
||
|
```cshtml title="Shared/MainLayout.razor"
|
||
|
@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.
|
||
|
|
||
|
```cshtml title="Pages/ThemedCounter.razor"
|
||
|
@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`:
|
||
|
|
||
|
```cshtml
|
||
|
<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:
|
||
|
|
||
|
```cshtml
|
||
|
...
|
||
|
|
||
|
@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`.
|
||
|
|
||
|
```csharp title="UIInterfaces/ITab.cs"
|
||
|
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.
|
||
|
|
||
|
```cshtml title="Shared/TabSet.razor"
|
||
|
@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.
|
||
|
|
||
|
```cshtml title="Shared/Tab.razor"
|
||
|
@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.
|
||
|
|
||
|
```cshtml title="Pages/ExampleTabSet.razor"
|
||
|
@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;
|
||
|
}
|
||
|
```
|