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.
Blazor/Documentation/docusaurus/docs/delete-item/create-confirm-popup.md

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;
}