Skip to content
This repository has been archived by the owner on Nov 1, 2024. It is now read-only.

Commit

Permalink
Refactor ViewModels
Browse files Browse the repository at this point in the history
  • Loading branch information
YuriyDurov committed May 8, 2024
1 parent 95cc86c commit 6b6d4e9
Show file tree
Hide file tree
Showing 14 changed files with 228 additions and 163 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
<RootNamespace>BitzArt.Blazor.MVVM.SampleApp.Client</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.2" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\BitzArt.Blazor.MVVM\BitzArt.Blazor.MVVM.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\BitzArt.Blazor.MVVM\BitzArt.Blazor.MVVM.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
@inherits ComponentBase<CounterViewModel>

@if (ViewModel.State is not null)
{
<p role="status">Current count: @ViewModel.State.Count</p>

<button class="btn btn-primary" @onclick="ViewModel.IncrementCount">Click me</button>
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@page "/counter"
@rendermode @(new InteractiveAutoRenderMode(prerender: true))
@rendermode @(new InteractiveWebAssemblyRenderMode(prerender: true))
@inherits PageBase<CounterPageViewModel>

<PageTitle>Counter</PageTitle>
Expand All @@ -10,8 +10,7 @@

<p>@ViewModel.State?.Text</p>

<p role="status">Current count: @ViewModel.State?.Count</p>

<button class="btn btn-primary" @onclick="ViewModel.IncrementCount">Click me</button>
<Counter ViewModel="ViewModel.Counter1ViewModel" />
<Counter ViewModel="ViewModel.Counter2ViewModel" />

<ComponentStateContainer ViewModel="ViewModel" />
Original file line number Diff line number Diff line change
@@ -1,35 +1,32 @@
namespace BitzArt.Blazor.MVVM.SampleApp;

public class CounterPageViewModel : ComponentViewModel<CounterPageViewModelState>
public class CounterPageViewModel(
CounterViewModel counter1ViewModel,
CounterViewModel counter2ViewModel,
RenderingEnvironment renderingEnvironment)
: ViewModel<CounterPageViewModelState>
{
private readonly Timer _timer;

public CounterPageViewModel()
{
_timer = new Timer(TimerIncrementCount, null, 1000, 1000);
}

private void TimerIncrementCount(object? state)
{
State.Count++;
StateHasChanged();
}
public CounterViewModel Counter1ViewModel { get; } = counter1ViewModel;
public CounterViewModel Counter2ViewModel { get; } = counter2ViewModel;

public override void InitializeState()
{
State.Count = 0;
State.Text = $"ViewModel State initialized on: {RenderingEnvironment}";
State.Text = $"ViewModel State initialized on: {renderingEnvironment}";

OnStateRestored();
Counter2ViewModel.State!.Count += 100;
}

public void IncrementCount()
public override void OnStateRestored()
{
State.Count++;
Counter1ViewModel.State = State.Counter1State;
Counter2ViewModel.State = State.Counter2State;
}
}

public class CounterPageViewModelState
{
public int? Count { get; set; } = null;

public string? Text { get; set; } = "State not initialized";
public string Text { get; set; } = "State not initialized";
public CounterState Counter1State { get; set; } = new();
public CounterState Counter2State { get; set; } = new();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
namespace BitzArt.Blazor.MVVM.SampleApp;

public class CounterViewModel : ViewModel
{
public CounterState? State { get; set; }

private readonly Timer _timer;

public CounterViewModel()
{
_timer = new Timer(TimerIncrementCount, null, 1000, 1000);
}

private void TimerIncrementCount(object? state)
{
if (State is null) return;

State.Count++;
StateHasChanged();
}

public void IncrementCount()
{
if (State is null) return;

State.Count++;
StateHasChanged();
}
}

public class CounterState
{
public int Count { get; set; } = 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using BitzArt.Blazor.MVVM.SampleApp.Client
@using BitzArt.Blazor.MVVM
@using BitzArt.Blazor.MVVM.SampleApp.Client.Components
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>BitzArt.Blazor.MVVM.SampleApp</RootNamespace>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\BitzArt.Blazor.MVVM.SampleApp.Client\BitzArt.Blazor.MVVM.SampleApp.Client.csproj" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BitzArt.Blazor.MVVM.SampleApp.Client\BitzArt.Blazor.MVVM.SampleApp.Client.csproj" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.2" />
</ItemGroup>

</Project>
6 changes: 3 additions & 3 deletions src/BitzArt.Blazor.MVVM/ComponentStateContainer.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace BitzArt.Blazor.MVVM;

public partial class ComponentStateContainer : ComponentBase
{
[Parameter] public ComponentViewModel ViewModel { get; set; } = null!;
[Parameter] public ViewModel ViewModel { get; set; } = null!;
[Parameter] public string StateKey { get; set; } = "state";

protected override void BuildRenderTree(RenderTreeBuilder builder)
Expand All @@ -21,15 +21,15 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
return SerializeComponentState(ViewModel, StateKey, strict: false);
}

private string? SerializeComponentState(ComponentViewModel viewModel, string key, bool strict = true)
private string? SerializeComponentState(ViewModel viewModel, string key, bool strict = true)
{
if (ViewModel is not IStatefulViewModel statefulViewModel)
{
if (strict) throw new InvalidOperationException($"ViewModel '{viewModel.GetType().Name}' must implement IStatefulViewModel");
return null;
}

return Serialize(statefulViewModel.ComponentState, key);
return Serialize(statefulViewModel.State, key);
}

private static string? Serialize(object state, string key)
Expand Down
4 changes: 2 additions & 2 deletions src/BitzArt.Blazor.MVVM/Extensions/AddViewModelExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public static IServiceCollection AddBlazorViewModelsFromAssembly(this IServiceCo
.GetTypes()
.Where(t => t.IsPublic)
.Where(t => !t.IsAbstract)
.Where(t => t.IsSubclassOf(typeof(ComponentViewModel)));
.Where(t => t.IsSubclassOf(typeof(ViewModel)));

foreach (var viewModelType in viewModelTypes) services.AddTransient(viewModelType);

Expand All @@ -63,7 +63,7 @@ public static IServiceCollection AddBlazorViewModel(this IServiceCollection serv
/// Adds a view model to the service collection.
/// </summary>
public static IServiceCollection AddBlazorViewModel<TViewModel>(this IServiceCollection services)
where TViewModel : ComponentViewModel
where TViewModel : ViewModel
{
services.AddTransient<TViewModel>();
return services;
Expand Down
3 changes: 2 additions & 1 deletion src/BitzArt.Blazor.MVVM/Interfaces/IStatefulViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
internal interface IStatefulViewModel
{
public Type StateType { get; }
public object ComponentState { get; set; }
public object State { get; set; }

public void InitializeState();
public Task InitializeStateAsync();
public void OnStateRestored();
}
44 changes: 44 additions & 0 deletions src/BitzArt.Blazor.MVVM/Models/ComponentBase{TViewModel}.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
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]
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.OnStateHasChanged += () => InvokeAsync(StateHasChanged);
await RestoreStateAsync();
}

protected virtual Task RestoreStateAsync() => Task.CompletedTask;

void IStateComponent.StateHasChanged() => InvokeAsync(StateHasChanged);
}
69 changes: 0 additions & 69 deletions src/BitzArt.Blazor.MVVM/Models/ComponentViewModel.cs

This file was deleted.

Loading

0 comments on commit 6b6d4e9

Please sign in to comment.