diff --git a/docs/en/UI/Blazor/Page-Progress.md b/docs/en/UI/Blazor/Page-Progress.md index 149c35293e..14daa41d3c 100644 --- a/docs/en/UI/Blazor/Page-Progress.md +++ b/docs/en/UI/Blazor/Page-Progress.md @@ -1,3 +1,62 @@ # Blazor UI: Page Progress -TODO \ No newline at end of file +Page progress is the newest component introduced with Blazorise and ABP Framework. It is used to show scoped page progress on top of the page and to indicate to the user that currently a long running process is in the work. By default you don't need to do anything to show the progress indicator, as all the work is done automatically by the ABP Framework internals. This means that all calls to the ABP backend(through Rest API) will activate page progress and show the loading indicator. + +This doesn't mean that you don't have the control over it. On the contrary. If you also want to show progress for your own processes, it is really easy to do. All you have to do is to use `IUiPageProgressService`. + +To use `IUiPageProgressService` you need to inject it into your own Blazor component or page. You have two options of injection. + +1. Parameter injection +2. Constructor injection + +Whichever you chose the end result is the same. In this guide we're going to use parameter injection. So let us begin. + +## Example + +First, inject the `IUiPageProgressService` + +```cs +@inject IUiPageProgressService pageProgressService +``` + +Next, all you have to do is invoke the `Go` method in `IUiPageProgressService`. It's that simple. + +```cs +Task OnClick() +{ + return pageProgressService.Go(null); +} +``` + +The previous example will show the progress with a default settings. If, for example you want to change the progress color you can override it by setting the options through the `Go` method. + +```cs +Task OnClick() +{ + return pageProgressService.Go(null, options => + { + options.Type = UiPageProgressType.Warning; + }); +} +``` + +## Breakdown + +The first parameter of the `Go` needs a little explanation. In the previous example we have set it to `null` which means, once called it will show an _indeterminate_ indicator and will cycle the loading animation indefinitely, until we hide the progress. You also have the option of defining the actual percentage of the progress and the code is the same, just instead of sending it the `null` you will send it a number between `0` and `100`. + +```cs +pageProgressService.Go(25) +``` + +### Valid values + +1. `null` - show _indeterminate_ indicator +2. `>= 0` and `<= 100` - show the regular _percentage_ progress + +## Hiding progress + +To hide the progress just set the actual values to something other then the _Valid value_. + +```cs +pageProgressService.Go(-1) +``` \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/MainLayout.razor b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/MainLayout.razor index e85e899123..5993d17e0d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/MainLayout.razor +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/MainLayout.razor @@ -21,4 +21,5 @@ + diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/AspNetCore/Components/WebAssembly/Hosting/AbpWebAssemblyHostBuilderExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/AspNetCore/Components/WebAssembly/Hosting/AbpWebAssemblyHostBuilderExtensions.cs index 7cba7ce9b3..9e1752ffda 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/AspNetCore/Components/WebAssembly/Hosting/AbpWebAssemblyHostBuilderExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/AspNetCore/Components/WebAssembly/Hosting/AbpWebAssemblyHostBuilderExtensions.cs @@ -1,6 +1,5 @@ using System; using System.Globalization; -using System.Reflection; using System.Runtime.CompilerServices; using System.Threading.Tasks; using JetBrains.Annotations; @@ -8,7 +7,9 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Volo.Abp; using Volo.Abp.AspNetCore.Components.WebAssembly; +using Volo.Abp.AspNetCore.Components.WebAssembly.DependencyInjection; using Volo.Abp.AspNetCore.Mvc.Client; +using Volo.Abp.DependencyInjection; using Volo.Abp.Modularity; namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting @@ -39,13 +40,17 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting return application; } - public async static Task InitializeAsync( + public static async Task InitializeAsync( [NotNull] this IAbpApplicationWithExternalServiceProvider application, [NotNull] IServiceProvider serviceProvider) { Check.NotNull(application, nameof(application)); Check.NotNull(serviceProvider, nameof(serviceProvider)); + var serviceProviderAccessor = (WebAssemblyClientScopeServiceProviderAccessor) + serviceProvider.GetRequiredService(); + serviceProviderAccessor.ServiceProvider = serviceProvider; + application.Initialize(serviceProvider); using (var scope = serviceProvider.CreateScope()) @@ -55,7 +60,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting } } - private async static Task InitializeModulesAsync(IServiceProvider serviceProvider) + private static async Task InitializeModulesAsync(IServiceProvider serviceProvider) { foreach (var service in serviceProvider.GetServices()) { @@ -63,7 +68,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting } } - private async static Task SetCurrentLanguageAsync(IServiceScope scope) + private static async Task SetCurrentLanguageAsync(IServiceScope scope) { var configurationClient = scope.ServiceProvider.GetRequiredService(); var utilsService = scope.ServiceProvider.GetRequiredService(); diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpBlazorClientHttpMessageHandler.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpBlazorClientHttpMessageHandler.cs index 84f9743a1e..7b40ca5286 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpBlazorClientHttpMessageHandler.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpBlazorClientHttpMessageHandler.cs @@ -4,8 +4,9 @@ using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.JSInterop; +using Volo.Abp.AspNetCore.Components.Progression; using Volo.Abp.DependencyInjection; namespace Volo.Abp.AspNetCore.Components.WebAssembly @@ -18,6 +19,8 @@ namespace Volo.Abp.AspNetCore.Components.WebAssembly private readonly NavigationManager _navigationManager; + private readonly IUiPageProgressService _uiPageProgressService; + private const string AntiForgeryCookieName = "XSRF-TOKEN"; private const string AntiForgeryHeaderName = "RequestVerificationToken"; @@ -25,19 +28,33 @@ namespace Volo.Abp.AspNetCore.Components.WebAssembly public AbpBlazorClientHttpMessageHandler( IJSRuntime jsRuntime, ICookieService cookieService, - NavigationManager navigationManager) + NavigationManager navigationManager, + IClientScopeServiceProviderAccessor clientScopeServiceProviderAccessor) { _jsRuntime = jsRuntime; _cookieService = cookieService; _navigationManager = navigationManager; + _uiPageProgressService = clientScopeServiceProviderAccessor.ServiceProvider.GetRequiredService(); } - protected async override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - await SetLanguageAsync(request, cancellationToken); - await SetAntiForgeryTokenAsync(request); + try + { + await _uiPageProgressService.Go(null, options => + { + options.Type = UiPageProgressType.Info; + }); + + await SetLanguageAsync(request, cancellationToken); + await SetAntiForgeryTokenAsync(request); - return await base.SendAsync(request, cancellationToken); + return await base.SendAsync(request, cancellationToken); + } + finally + { + await _uiPageProgressService.Go(-1); + } } private async Task SetLanguageAsync(HttpRequestMessage request, CancellationToken cancellationToken) diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/DependencyInjection/WebAssemblyClientScopeServiceProviderAccessor.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/DependencyInjection/WebAssemblyClientScopeServiceProviderAccessor.cs new file mode 100644 index 0000000000..74b16ec951 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/DependencyInjection/WebAssemblyClientScopeServiceProviderAccessor.cs @@ -0,0 +1,12 @@ +using System; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.Components.WebAssembly.DependencyInjection +{ + public class WebAssemblyClientScopeServiceProviderAccessor : + IClientScopeServiceProviderAccessor, + ISingletonDependency + { + public IServiceProvider ServiceProvider { get; set; } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/IUiPageProgressService.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/IUiPageProgressService.cs new file mode 100644 index 0000000000..fa9781e2d9 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/IUiPageProgressService.cs @@ -0,0 +1,21 @@ +using System; +using System.Threading.Tasks; + +namespace Volo.Abp.AspNetCore.Components.Progression +{ + public interface IUiPageProgressService + { + /// + /// An event raised after the notification is received. + /// + public event EventHandler ProgressChanged; + + /// + /// Sets the progress percentage. + /// + /// Value of the progress from 0 to 100, or null for indeterminate progress. + /// Additional options. + /// Awaitable task. + Task Go(int? percentage, Action options = null); + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/NullUiPageProgressService.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/NullUiPageProgressService.cs new file mode 100644 index 0000000000..5c375e2b40 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/NullUiPageProgressService.cs @@ -0,0 +1,16 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.Components.Progression +{ + public class NullUiPageProgressService : IUiPageProgressService, ISingletonDependency + { + public event EventHandler ProgressChanged; + + public Task Go(int? percentage, Action options = null) + { + return Task.CompletedTask; + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressEventArgs.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressEventArgs.cs new file mode 100644 index 0000000000..74af5cb971 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressEventArgs.cs @@ -0,0 +1,17 @@ +using System; + +namespace Volo.Abp.AspNetCore.Components.Progression +{ + public class UiPageProgressEventArgs : EventArgs + { + public UiPageProgressEventArgs(int? percentage, UiPageProgressOptions options) + { + Percentage = percentage; + Options = options; + } + + public int? Percentage { get; } + + public UiPageProgressOptions Options { get; } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressOptions.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressOptions.cs new file mode 100644 index 0000000000..0ce243552d --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressOptions.cs @@ -0,0 +1,13 @@ +namespace Volo.Abp.AspNetCore.Components.Progression +{ + /// + /// Options to override page progress appearance. + /// + public class UiPageProgressOptions + { + /// + /// Type or color, of the page progress. + /// + public UiPageProgressType Type { get; set; } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressType.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressType.cs new file mode 100644 index 0000000000..db1d00932d --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Progression/UiPageProgressType.cs @@ -0,0 +1,11 @@ +namespace Volo.Abp.AspNetCore.Components.Progression +{ + public enum UiPageProgressType + { + Default, + Info, + Success, + Warning, + Error, + } +} diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/DependencyInjection/HttpContextClientScopeServiceProviderAccessor.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/DependencyInjection/HttpContextClientScopeServiceProviderAccessor.cs new file mode 100644 index 0000000000..943c11ebc3 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/DependencyInjection/HttpContextClientScopeServiceProviderAccessor.cs @@ -0,0 +1,33 @@ +using System; +using Microsoft.AspNetCore.Http; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.DependencyInjection +{ + public class HttpContextClientScopeServiceProviderAccessor : + IClientScopeServiceProviderAccessor, + ISingletonDependency + { + public IServiceProvider ServiceProvider + { + get + { + var httpContext = _httpContextAccessor.HttpContext; + if (httpContext == null) + { + throw new AbpException("HttpContextClientScopeServiceProviderAccessor should only be used in a web request scope!"); + } + + return httpContext.RequestServices; + } + } + + private readonly IHttpContextAccessor _httpContextAccessor; + + public HttpContextClientScopeServiceProviderAccessor( + IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + } +} diff --git a/framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs b/framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs index 9a820600bd..4a4d4df722 100644 --- a/framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs +++ b/framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs @@ -8,15 +8,12 @@ using JetBrains.Annotations; using Localization.Resources.AbpUi; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Components; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; using Volo.Abp.AspNetCore.Components; -using Volo.Abp.AspNetCore.Components.WebAssembly; using Volo.Abp.Authorization; using Volo.Abp.BlazoriseUI.Components; -using Volo.Abp.ObjectMapping; namespace Volo.Abp.BlazoriseUI { diff --git a/framework/src/Volo.Abp.BlazoriseUI/BlazoriseUiPageProgressService.cs b/framework/src/Volo.Abp.BlazoriseUI/BlazoriseUiPageProgressService.cs new file mode 100644 index 0000000000..5285669c00 --- /dev/null +++ b/framework/src/Volo.Abp.BlazoriseUI/BlazoriseUiPageProgressService.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.AspNetCore.Components.Progression; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.BlazoriseUI +{ + [Dependency(ReplaceServices = true)] + public class BlazoriseUiPageProgressService : IUiPageProgressService, + IScopedDependency + { + /// + /// An event raised after the notification is received. + /// + public event EventHandler ProgressChanged; + + public Task Go(int? percentage, Action options = null) + { + var uiPageProgressOptions = CreateDefaultOptions(); + options?.Invoke(uiPageProgressOptions); + + ProgressChanged?.Invoke(this, new UiPageProgressEventArgs(percentage, uiPageProgressOptions)); + + return Task.CompletedTask; + } + + protected virtual UiPageProgressOptions CreateDefaultOptions() + { + return new UiPageProgressOptions(); + } + } +} diff --git a/framework/src/Volo.Abp.BlazoriseUI/Components/UiPageProgress.razor b/framework/src/Volo.Abp.BlazoriseUI/Components/UiPageProgress.razor new file mode 100644 index 0000000000..91799182dd --- /dev/null +++ b/framework/src/Volo.Abp.BlazoriseUI/Components/UiPageProgress.razor @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlazoriseUI/Components/UiPageProgress.razor.cs b/framework/src/Volo.Abp.BlazoriseUI/Components/UiPageProgress.razor.cs new file mode 100644 index 0000000000..8a0a8e28d2 --- /dev/null +++ b/framework/src/Volo.Abp.BlazoriseUI/Components/UiPageProgress.razor.cs @@ -0,0 +1,58 @@ +using System; +using Blazorise; +using Microsoft.AspNetCore.Components; +using Volo.Abp.AspNetCore.Components.Progression; + +namespace Volo.Abp.BlazoriseUI.Components +{ + public partial class UiPageProgress : ComponentBase, IDisposable + { + protected PageProgress PageProgressRef { get; set; } + + protected int? Percentage { get; set; } + + protected bool Visible { get; set; } + + protected Color Color { get; set; } + + [Inject] protected IUiPageProgressService UiPageProgressService { get; set; } + + protected override void OnInitialized() + { + base.OnInitialized(); + + UiPageProgressService.ProgressChanged += OnProgressChanged; + } + + private async void OnProgressChanged(object sender, UiPageProgressEventArgs e) + { + Percentage = e.Percentage; + Visible = e.Percentage == null || (e.Percentage >= 0 && e.Percentage <= 100); + Color = GetColor(e.Options.Type); + + await PageProgressRef.SetValueAsync(e.Percentage); + + await InvokeAsync(StateHasChanged); + } + + public virtual void Dispose() + { + if (UiPageProgressService != null) + { + UiPageProgressService.ProgressChanged -= OnProgressChanged; + } + } + + protected virtual Color GetColor(UiPageProgressType pageProgressType) + { + return pageProgressType switch + { + UiPageProgressType.Info => Color.Info, + UiPageProgressType.Success => Color.Success, + UiPageProgressType.Warning => Color.Warning, + UiPageProgressType.Error => Color.Danger, + _ => Color.None, + }; + } + } +} diff --git a/framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj b/framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj index 1dce47face..de793e078f 100644 --- a/framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj +++ b/framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj @@ -12,9 +12,9 @@ - - - + + + diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IClientScopeServiceProviderAccessor.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IClientScopeServiceProviderAccessor.cs new file mode 100644 index 0000000000..7c1d1ed08d --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IClientScopeServiceProviderAccessor.cs @@ -0,0 +1,9 @@ +using System; + +namespace Volo.Abp.DependencyInjection +{ + public interface IClientScopeServiceProviderAccessor + { + IServiceProvider ServiceProvider { get; } + } +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyCompanyName.MyProjectName.Blazor.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyCompanyName.MyProjectName.Blazor.csproj index 9bc6fb7092..8ac2e020e5 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyCompanyName.MyProjectName.Blazor.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyCompanyName.MyProjectName.Blazor.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/MyCompanyName.MyProjectName.Blazor.Host.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/MyCompanyName.MyProjectName.Blazor.Host.csproj index 444e49801f..6bd1d01269 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/MyCompanyName.MyProjectName.Blazor.Host.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/MyCompanyName.MyProjectName.Blazor.Host.csproj @@ -8,8 +8,8 @@ - - + +