New to Telerik UI for BlazorStart a free 30-day trial

How to Prompt Users for Unsaved Changes on Navigation

Environment

ProductForm for Blazor

Description

This knowledge base article answers the following questions:

  • How to detect unsaved changes in a Blazor TelerikForm?
  • How to prompt users before navigating away from unsaved changes in Form?
  • How to conditionally navigate away from a form?

Solution

To prompt the users with a warning message when they attempt to navigate away from a page with unsaved changes in a TelerikForm, follow these steps:

  1. Inject NavigationManager to handle navigation
  2. Register a handler using NavigationManager.RegisterLocationChangingHandler() to intercept navigation attempts
  3. Use the LocationChangingHandler to check if the form has unsaved changes
  4. Add a <TelerikDialog> to prompt the user when there are unsaved changes
  5. Implement a PreventLeaving() method and ProceedNavigation() method to close the dialog without navigating and to manually navigate to the stored URL if the user confirms
@implements IDisposable
@inject NavigationManager NavigationManager

<TelerikForm @ref="@FormRef"
             Model="@Employee"
             Width="300px">
    <FormItems>
        <FormItem Field="@nameof(Person.Id)" Enabled="false"></FormItem>
        <FormAutoGeneratedItems />
    </FormItems>
</TelerikForm>

<TelerikButton OnClick="@(() => NavigateToExternalPage())">
    Go To Other Page
</TelerikButton>


<TelerikDialog @bind-Visible="@ShowNavigationDialog"
               Title="Confirm Navigation">
    <DialogContent>
        You have unsaved changes. Are you sure you want to leave this page?
    </DialogContent>
    <DialogButtons>
        <TelerikButton OnClick="@PreventLeaving">No</TelerikButton>
        <TelerikButton ThemeColor="@ThemeConstants.Button.ThemeColor.Primary" OnClick="@ProceedNavigation">Yes</TelerikButton>
    </DialogButtons>
</TelerikDialog>

@code {
    private Person Employee = new Person();
    private TelerikForm? FormRef { get; set; }
    private IDisposable? NavEventRegistration;
    private bool ShowNavigationDialog = false;
    private string? NextUrl;
    private bool isNavigationConfirmed = false; // Flag to track navigation confirmation

    private ValueTask LocationChangingHandler(LocationChangingContext args)
    {
        // Prevent the confirmation dialog from appearing again once the user confirmed navigation
        if (isNavigationConfirmed)
        {
            return ValueTask.CompletedTask;
        }

        if (FormRef?.EditContext.IsModified() ?? false)
        {
            // Prevent navigation and store the target URL
            args.PreventNavigation();
            NextUrl = args.TargetLocation;
            ShowNavigationDialog = true;

            // Force Blazor to re-render
            InvokeAsync(StateHasChanged);
        }

        return ValueTask.CompletedTask;
    }

    private void NavigateToExternalPage()
    {
        NavigationManager.NavigateTo("https://www.telerik.com/blazor-ui/documentation/introduction", forceLoad: true);
    }

    private void PreventLeaving()
    {
        // Simply close the dialog without changing the page
        ShowNavigationDialog = false;
        StateHasChanged();
    }

    private void ProceedNavigation()
    {
        // Set the flag to indicate navigation is confirmed
        isNavigationConfirmed = true;

        // Navigate manually to the stored target URL
        if (!string.IsNullOrEmpty(NextUrl))
        {
            NavigationManager.NavigateTo(NextUrl);
        }

        // Close the dialog after confirming navigation
        ShowNavigationDialog = false;
    }

    protected override void OnInitialized()
    {
        Employee = new Person()
        {
            Id = 1,
            FirstName = "John",
            LastName = "Doe",
            BirthDate = DateTime.Today.AddYears(-30)
        };

        // Register the navigation handler
        NavEventRegistration = NavigationManager.RegisterLocationChangingHandler(LocationChangingHandler);
    }

    public void Dispose()
    {
        NavEventRegistration?.Dispose();
    }

    public class Person
    {
        public int Id { get; set; }
        public string FirstName { get; set; } = string.Empty;
        public string LastName { get; set; } = string.Empty;
        public DateTime BirthDate { get; set; } = DateTime.Today;
    }
}

See Also