This repository has been archived by the owner on Nov 1, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rework state management and injections
commit 9a00c1e Author: Yuriy Durov <[email protected]> Date: Wed May 22 21:09:44 2024 +0400 Rework state management commit ecfddfe Author: Vladimir Seldemirov <[email protected]> Date: Wed May 22 13:37:33 2024 +0400 Split state restoring
- Loading branch information
1 parent
c75a630
commit d49f102
Showing
29 changed files
with
567 additions
and
196 deletions.
There are no files selected for viewing
13 changes: 10 additions & 3 deletions
13
...tzArt.Blazor.MVVM.SampleApp/BitzArt.Blazor.MVVM.SampleApp.Client/Components/Counter.razor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,15 @@ | ||
@inherits ComponentBase<CounterViewModel> | ||
|
||
@if (ViewModel.State is not null) | ||
{ | ||
<div class="card"> | ||
|
||
<h4>@ViewModel.NameOnPage</h4> | ||
|
||
<p role="status">Current count: @ViewModel.State.Count</p> | ||
|
||
<button class="btn btn-primary" @onclick="ViewModel.IncrementCount">Click me</button> | ||
} | ||
|
||
<small style="margin-top:1rem;">@ViewModel.State.Text</small> | ||
|
||
<small style="margin-top:1rem;">@Description</small> | ||
|
||
</div> |
9 changes: 9 additions & 0 deletions
9
...rt.Blazor.MVVM.SampleApp/BitzArt.Blazor.MVVM.SampleApp.Client/Components/Counter.razor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
using Microsoft.AspNetCore.Components; | ||
|
||
namespace BitzArt.Blazor.MVVM.SampleApp.Client.Components; | ||
|
||
public partial class Counter : ComponentBase<CounterViewModel> | ||
{ | ||
[Parameter, EditorRequired] | ||
public string? Description { get; set; } | ||
} |
12 changes: 12 additions & 0 deletions
12
...t.Blazor.MVVM.SampleApp/BitzArt.Blazor.MVVM.SampleApp.Client/Components/Counter.razor.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
.card { | ||
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); | ||
transition: 0.3s; | ||
padding: 1rem; | ||
border-radius: 0.5rem; | ||
margin-bottom: 2rem; | ||
width: 400px; | ||
} | ||
|
||
.card:hover { | ||
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 18 additions & 11 deletions
29
...or.MVVM.SampleApp/BitzArt.Blazor.MVVM.SampleApp.Client/ViewModels/CounterPageViewModel.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,32 @@ | ||
using Microsoft.AspNetCore.Components; | ||
namespace BitzArt.Blazor.MVVM.SampleApp; | ||
|
||
namespace BitzArt.Blazor.MVVM.SampleApp; | ||
|
||
public class CounterPageViewModel( | ||
RenderingEnvironment renderingEnvironment) | ||
public class CounterPageViewModel(RenderingEnvironment renderingEnvironment) | ||
: ViewModel<CounterPageViewModelState> | ||
{ | ||
[Inject] | ||
[NestViewModel] | ||
public CounterViewModel Counter1 { get; set; } = null!; | ||
|
||
[Inject] | ||
[NestViewModel] | ||
public CounterViewModel Counter2 { get; set; } = null!; | ||
|
||
[NestViewModel] | ||
public CounterViewModel Counter3 { get; set; } = null!; | ||
|
||
public override void InitializeState() | ||
{ | ||
State.Text = $"ViewModel State initialized on: {renderingEnvironment}"; | ||
Counter2.State!.Count = 100; | ||
State.Text = $"Page State initialized on: {renderingEnvironment}"; | ||
} | ||
|
||
protected override void OnDependenciesInjected() | ||
{ | ||
Counter2.OnStateInitialized += (_) => | ||
{ | ||
Counter2.State!.Count += 100; | ||
}; | ||
} | ||
} | ||
|
||
public class CounterPageViewModelState | ||
public class CounterPageViewModelState : ComponentState | ||
{ | ||
public string Text { get; set; } = "State not initialized"; | ||
public string Text { get; set; } = "Page State not initialized"; | ||
} |
41 changes: 27 additions & 14 deletions
41
...Blazor.MVVM.SampleApp/BitzArt.Blazor.MVVM.SampleApp.Client/ViewModels/CounterViewModel.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,50 @@ | ||
namespace BitzArt.Blazor.MVVM.SampleApp; | ||
|
||
public class CounterViewModel : ViewModel<CounterState> | ||
public class CounterViewModel(RenderingEnvironment renderingEnvironment) | ||
: ViewModel<CounterState>, IDisposable | ||
{ | ||
private readonly Timer _timer; | ||
[ParentViewModel] | ||
public CounterPageViewModel Parent { get; set; } = null!; | ||
|
||
public CounterViewModel() | ||
private Timer? _timer; | ||
|
||
public string NameOnPage | ||
{ | ||
_timer = new Timer(TimerIncrementCount, null, 1000, 1000); | ||
get | ||
{ | ||
if (this == Parent.Counter1) return "Counter 1"; | ||
if (this == Parent.Counter2) return "Counter 2"; | ||
if (this == Parent.Counter3) return "Counter 3"; | ||
return string.Empty; | ||
} | ||
} | ||
|
||
public override void InitializeState() | ||
{ | ||
State.Count = 0; | ||
State.Text = $"Counter State initialized on: {renderingEnvironment}"; | ||
} | ||
|
||
private void TimerIncrementCount(object? state) | ||
protected override void OnDependenciesInjected() | ||
{ | ||
if (State is null) return; | ||
if (this == Parent.Counter3) _timer = new Timer(IncrementCount, null, 1000, 1000); | ||
} | ||
|
||
public void IncrementCount(object? state = null) | ||
{ | ||
State.Count++; | ||
ComponentStateHasChanged(); | ||
} | ||
|
||
public void IncrementCount() | ||
public void Dispose() | ||
{ | ||
if (State is null) return; | ||
|
||
State.Count++; | ||
ComponentStateHasChanged(); | ||
_timer?.Dispose(); | ||
GC.SuppressFinalize(this); | ||
} | ||
} | ||
|
||
public class CounterState | ||
public class CounterState : ComponentState | ||
{ | ||
public int? Count { get; set; } | ||
public int Count { get; set; } = 0; | ||
|
||
public string Text { get; set; } = "Counter State not initialized"; | ||
} |
2 changes: 1 addition & 1 deletion
2
...tzArt.Blazor.MVVM.SampleApp/BitzArt.Blazor.MVVM.SampleApp/Components/Layout/NavMenu.razor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
namespace BitzArt.Blazor.MVVM; | ||
|
||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] | ||
public sealed class NestViewModelAttribute : Attribute | ||
{ | ||
} |
7 changes: 7 additions & 0 deletions
7
src/BitzArt.Blazor.MVVM/Attributes/ParentViewModelAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace BitzArt.Blazor.MVVM; | ||
|
||
// TODO: Implement ParentAttribute | ||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] | ||
public sealed class ParentViewModelAttribute : Attribute | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
src/BitzArt.Blazor.MVVM/Components/ComponentBase{TViewModel}.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
using Microsoft.AspNetCore.Components; | ||
using Microsoft.JSInterop; | ||
|
||
namespace BitzArt.Blazor.MVVM; | ||
|
||
public abstract class ComponentBase<TViewModel> : ComponentBase, IStateComponent | ||
where TViewModel : ViewModel | ||
{ | ||
[Parameter, EditorRequired] | ||
public TViewModel ViewModel { get; set; } = null!; | ||
|
||
[Inject] | ||
protected IServiceProvider ServiceProvider { get; set; } = null!; | ||
|
||
[Inject] | ||
protected IJSRuntime Js { get; set; } = default!; | ||
|
||
[Inject] | ||
internal PageStateDictionaryContainer PageStateDictionaryContainer { get; set; } = null!; | ||
|
||
[Inject] | ||
private protected BlazorViewModelStateManager StateManager { get; set; } = null!; | ||
|
||
[Inject] | ||
protected RenderingEnvironment RenderingEnvironment { get; set; } = null!; | ||
|
||
/// <summary> | ||
/// Navigation manager. | ||
/// </summary> | ||
[Inject] | ||
protected NavigationManager NavigationManager { get; set; } = null!; | ||
|
||
/// <summary> | ||
/// Method invoked when the component is ready to start, having received its initial | ||
/// parameters from its parent in the render tree. Override this method if you will | ||
/// perform an asynchronous operation and want the component to refresh when that | ||
/// operation is completed. | ||
/// </summary> | ||
/// <returns>A <see cref="Task"/> representing any asynchronous operation.</returns> | ||
protected override async Task OnInitializedAsync() | ||
{ | ||
await base.OnInitializedAsync(); | ||
ViewModel.OnComponentStateChanged += async (_) => | ||
{ | ||
await InvokeAsync(StateHasChanged); | ||
}; | ||
|
||
await SetupStateAsync(); | ||
} | ||
|
||
private async Task SetupStateAsync() | ||
{ | ||
var shouldRestoreState = ShouldRestoreState(); | ||
|
||
if (shouldRestoreState) await RestoreStateAsync(); | ||
else await InitializeStateAsync(); | ||
} | ||
|
||
protected virtual bool ShouldRestoreState() | ||
{ | ||
return !RenderingEnvironment.IsPrerender; | ||
} | ||
|
||
protected virtual async Task InitializeStateAsync() | ||
{ | ||
if (ViewModel is not IStatefulViewModel statefulViewModel) return; | ||
|
||
await StateManager.InitializeStateAsync(ViewModel); | ||
|
||
statefulViewModel.State.IsInitialized = true; | ||
|
||
statefulViewModel.OnStateInitialized?.Invoke(ViewModel); | ||
statefulViewModel.OnStateInitializedAsync?.Invoke(ViewModel); | ||
|
||
StateHasChanged(); | ||
} | ||
|
||
protected virtual async Task RestoreStateAsync() | ||
{ | ||
if (ViewModel is not IStatefulViewModel statefulViewModel) return; | ||
|
||
await PageStateDictionaryContainer!.WaitUntilConfiguredAsync(); | ||
var state = PageStateDictionaryContainer!.PageStateDictionary!.Get(ViewModel.Signature); | ||
if (state is null) | ||
{ | ||
await InitializeStateAsync(); | ||
return; | ||
} | ||
if (state!.GetType() != statefulViewModel.StateType) throw new InvalidOperationException("State type mismatch."); | ||
statefulViewModel.State = state; | ||
statefulViewModel.State.IsInitialized = true; | ||
} | ||
|
||
void IStateComponent.StateHasChanged() => InvokeAsync(StateHasChanged); | ||
} |
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.