From f10298363125786885bed5e00529f9ce3f2da23c Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Tue, 16 Jul 2019 13:56:06 -0700 Subject: [PATCH] Explicitly implement SetParametersAsync This change converts SetParametersAsync to be an explicit interface implmenentation, and adds lifecyle methods for running code before or after the parameters are set. This is a design prototype, and not ready to be merged. I'll add tests if we like the general direction of this. --- .../StandaloneApp/Pages/FetchData.razor | 3 +- .../Components/src/ComponentBase.cs | 55 ++++++++++++++++--- .../src/Forms/InputComponents/InputBase.cs | 9 +-- .../RouterTest/WithParameters.razor | 4 +- 4 files changed, 52 insertions(+), 19 deletions(-) diff --git a/src/Components/Blazor/testassets/StandaloneApp/Pages/FetchData.razor b/src/Components/Blazor/testassets/StandaloneApp/Pages/FetchData.razor index 9024a800a71b..5f1f5a6c1747 100644 --- a/src/Components/Blazor/testassets/StandaloneApp/Pages/FetchData.razor +++ b/src/Components/Blazor/testassets/StandaloneApp/Pages/FetchData.razor @@ -48,10 +48,9 @@ else WeatherForecast[] forecasts; - public override Task SetParametersAsync(ParameterCollection parameters) + protected override void OnParametersSetting() { StartDate = DateTime.Now; - return base.SetParametersAsync(parameters); } protected override async Task OnParametersSetAsync() diff --git a/src/Components/Components/src/ComponentBase.cs b/src/Components/Components/src/ComponentBase.cs index 045216b722c0..6c9a9dc4121b 100644 --- a/src/Components/Components/src/ComponentBase.cs +++ b/src/Components/Components/src/ComponentBase.cs @@ -78,7 +78,22 @@ protected virtual Task OnInitializedAsync() /// /// Method invoked when the component has received parameters from its parent in - /// the render tree, and the incoming values have been assigned to properties. + /// the render tree, before the incoming values have been assigned to properties. + /// + protected virtual void OnParametersSetting() + { + } + + /// + /// Method invoked when the component has received parameters from its parent in + /// the render tree, before the incoming values have been assigned to properties. + /// + /// A representing any asynchronous operation. + protected virtual Task OnParametersSettingAsync() => Task.CompletedTask; + + /// + /// Method invoked when the component has received parameters from its parent in + /// the render tree, after the incoming values have been assigned to properties. /// protected virtual void OnParametersSet() { @@ -86,7 +101,7 @@ protected virtual void OnParametersSet() /// /// Method invoked when the component has received parameters from its parent in - /// the render tree, and the incoming values have been assigned to properties. + /// the render tree, after the incoming values have been assigned to properties. /// /// A representing any asynchronous operation. protected virtual Task OnParametersSetAsync() @@ -175,22 +190,22 @@ void IComponent.Configure(RenderHandle renderHandle) /// Method invoked to apply initial or updated parameters to the component. /// /// The parameters to apply. - public virtual Task SetParametersAsync(ParameterCollection parameters) + Task IComponent.SetParametersAsync(ParameterCollection parameters) { - parameters.SetParameterProperties(this); if (!_initialized) { _initialized = true; - return RunInitAndSetParametersAsync(); + return RunInitAndSetParametersAsync(parameters); } else { - return CallOnParametersSetAsync(); + + return SetParametersCoreAsync(parameters); } } - private async Task RunInitAndSetParametersAsync() + private async Task RunInitAndSetParametersAsync(ParameterCollection parameters) { OnInitialized(); var task = OnInitializedAsync(); @@ -223,9 +238,35 @@ private async Task RunInitAndSetParametersAsync() // Don't call StateHasChanged here. CallOnParametersSetAsync should handle that for us. } + await SetParametersCoreAsync(parameters); + } + + private async Task SetParametersCoreAsync(ParameterCollection parameters) + { + await CallOnParametersSettingAsync(); + parameters.SetParameterProperties(this); await CallOnParametersSetAsync(); } + private Task CallOnParametersSettingAsync() + { + OnParametersSetting(); + var task = OnParametersSettingAsync(); + // If no async work is to be performed, i.e. the task has already ran to completion + // or was canceled by the time we got to inspect it, avoid going async and re-invoking + // StateHasChanged at the culmination of the async work. + var shouldAwaitTask = task.Status != TaskStatus.RanToCompletion && + task.Status != TaskStatus.Canceled; + + // We always call StateHasChanged here as we want to trigger a rerender after OnParametersSet and + // the synchronous part of OnParametersSetAsync has run. + StateHasChanged(); + + return shouldAwaitTask ? + CallStateHasChangedOnAsyncCompletion(task) : + Task.CompletedTask; + } + private Task CallOnParametersSetAsync() { OnParametersSet(); diff --git a/src/Components/Components/src/Forms/InputComponents/InputBase.cs b/src/Components/Components/src/Forms/InputComponents/InputBase.cs index 518876717fd5..849f8796a110 100644 --- a/src/Components/Components/src/Forms/InputComponents/InputBase.cs +++ b/src/Components/Components/src/Forms/InputComponents/InputBase.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq.Expressions; -using System.Threading.Tasks; namespace Microsoft.AspNetCore.Components.Forms { @@ -166,12 +165,9 @@ protected string CssClass } } - /// - public override Task SetParametersAsync(ParameterCollection parameters) + protected override void OnParametersSet() { - parameters.SetParameterProperties(this); - if (EditContext == null) { // This is the first run @@ -204,9 +200,6 @@ public override Task SetParametersAsync(ParameterCollection parameters) throw new InvalidOperationException($"{GetType()} does not support changing the " + $"{nameof(Forms.EditContext)} dynamically."); } - - // For derived components, retain the usual lifecycle with OnInit/OnParametersSet/etc. - return base.SetParametersAsync(ParameterCollection.Empty); } } } diff --git a/src/Components/test/testassets/BasicTestApp/RouterTest/WithParameters.razor b/src/Components/test/testassets/BasicTestApp/RouterTest/WithParameters.razor index e13b253ff1bb..64e8bb8ba6f2 100644 --- a/src/Components/test/testassets/BasicTestApp/RouterTest/WithParameters.razor +++ b/src/Components/test/testassets/BasicTestApp/RouterTest/WithParameters.razor @@ -1,5 +1,6 @@ @page "/WithParameters/Name/{firstName}" @page "/WithParameters/Name/{firstName}/LastName/{lastName}" +@implements IComponent
Your full name is @FirstName @LastName.
@@ -9,11 +10,10 @@ [Parameter] string LastName { get ; set; } - public override Task SetParametersAsync(ParameterCollection parameters) + protected override void OnParametersSetting() { // Manually reset parameters to defaults so we don't retain any from an earlier URL FirstName = default; LastName = default; - return base.SetParametersAsync(parameters); } }