Live Chat Icon For mobile
Live Chat Icon
Popular Categories.NET  (143).NET Core  (23)Angular  (39)ASP.NET  (47)ASP.NET Core  (53)ASP.NET MVC  (83)Azure  (27)Blazor  (49)DocIO  (18)Essential JS 2  (64)Essential Studio  (159)Flutter  (53)JavaScript  (134)Microsoft  (96)PDF  (48)React  (29)Succinctly series  (118)Syncfusion  (400)TypeScript  (30)Uno Platform  (2)UWP  (4)Vue  (25)Webinar  (14)Windows Forms  (52)WPF  (92)Xamarin  (105)XlsIO  (20)Other CategoriesBarcode  (4)BI  (29)Bold BI  (3)Build conference  (6)Business intelligence  (53)Button  (4)C#  (100)Chart  (40)Cloud  (8)Company  (445)Dashboard  (4)Data Science  (3)Data Validation  (2)DataGrid  (31)Development  (234)Doc  (7)eBook  (89)Enterprise  (22)Entity Framework  (5)Essential Tools  (14)Excel  (14)Extensions  (7)File Manager  (3)Gantt  (6)Gauge  (4)Git  (3)Grid  (24)HTML  (8)Installer  (1)Knockout  (2)LINQPad  (1)Linux  (1)M-Commerce  (1)Metro Studio  (11)Mobile  (140)Mobile MVC  (9)OLAP server  (1)Orubase  (12)Partners  (20)PDF viewer  (15)Performance  (2)PHP  (1)PivotGrid  (4)Predictive Analytics  (6)Report Server  (3)Reporting  (10)Reporting / Back Office  (11)Rich Text Editor  (5)Road Map  (9)Scheduler  (15)SfDataGrid  (7)Silverlight  (21)Sneak Peek  (12)Solution Services  (2)Spreadsheet  (2)SQL  (4)Stock Chart  (1)Surface  (4)Tablets  (5)Theme  (9)Tips and Tricks  (17)UI  (62)Uncategorized  (68)Unix  (2)User interface  (49)Visual State Manager  (1)Visual Studio  (12)Visual Studio Code  (7)Web  (99)What's new  (62)Windows 8  (19)Windows App  (1)Windows Phone  (15)Windows Phone 7  (9)WinRT  (26)
Share on twitter
Share on facebook
Share on linkedin
MVVM Pattern in Blazor For State Management – A Complete Guide

MVVM Pattern in Blazor For State Management – A Complete Guide

Sooner or later, when developing an app in the real world, you will need to share variables with different components, as well as preserve their value even when changing the displayed page. Passing variables between components or pages with parameters is not the best solution in complex situations. There are various ways to solve this issue, and in this post we will see how the Model-View-ViewModel pattern can be used for state management.

First, let’s figure out the application states in Blazor.

Application state

An application state is the state of a set of variables and their values at a specific time in a specific context.

Note: By default, Blazor can handle only internal states. This means that the set of variables is closely linked to the component in which the variables have been defined. When the component is disposed of, the variables will also be lost. Just to give an example, a simple page navigation triggers this scenario.

MVVM pattern

Let’s start with a quick presentation of the Model-View-ViewModel pattern.

MVVM was developed by Microsoft to simplify event programming in Windows Presentation Foundation (WPF). The architectural pattern was first announced in John Gossman’s blog in 2005. Today it is also used with other frameworks, like Xamarin.Forms for mobile application development.

Its goal is to separate the user interface code from the business logic code, so that it is easier to write, maintain, and test applications.

Key components

Model: An implementation of the application domain model that includes a data model along with validation logic. The model is responsible for handling the application’s data.

View: The view matches with the user interface and its code-behind. In Blazor, they are the Razor components.

ViewModel: The viewmodel provides data from the model in a way that the view can easily use it.

Binder: The binder allows the viewmodel and the view to constantly synchronize. This means that changes to the data made by the user through the view will be automatically reflected in the model, without the developer having to worry about it. Similarly, any changes made to the data in the viewmodel will be presented automatically by the view.

Please refer to the following image for a clear understanding of the MVVM pattern.
MVVM Pattern in Blazor

MVVM makes your application architecture loosely coupled. This means that you can change one layer without affecting the others.

The POCO classes that represent the model layer are easy to implement, nothing different from what we’re used to.

The views in Blazor pages and components must be linked to viewmodels by injection and binding. To update values in the UI from a model, something needs to be implemented in the viewmodel. That is the INotifyPropertyChanged interface.

INotifyPropertyChanged interface

This is used to notify the binding clients that a property value has been changed, in order to update them properly. This is the keystone of MVVM pattern development and in this way the pattern will be  implemented properly.

The easiest way to explain this development approach is with a sample project.

Sample project

For this purpose, I developed a simple Blazor server app that implements a simple to-do list with a title, date, notes, and information about the status of the task.

Here’s the tree structure of the project.
Project's tree structure

There are some differences from folder’s structure of the standard template. Here is the description of main folders:

  • Components: Contains the Razor components that are used in the application. In the MVVM perspective, these are views.
  • Pages: Contains the routable components. In the MVVM perspective, these are views, too.
  • Model: Contains POCO classes describing the data objects.
  • ViewModels: Contains all viewmodel classes and the base class from which they are inherited.

Viewmodel

Let’s talk about the most important class: viewmodel.

Step 1: First things first, implement the ToDoBasicViewModel class.

As I said before, the viewmodel should implement the INotifyPropertyChanged interface. The first line declares the PropertyChanged event, which is required by the interface.

public class ToDoBasicViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
}

In the OnPropertyChanged event handler, check if the propertyName in the argument is null. If not, trigger the event with the name of the updated property. Refer to the following code.

private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

Step 2: Here, I’ve added an IsBusy property that alerts the various components that an operation is in progress and other tasks should wait for the outcome. Therefore, any other interactions should be inhibited. It is not essential but will be useful. Refer to the following code example.

private bool isBusy =  false;
public bool IsBusy
{
    get => isBusy; 
    set
    {
        isBusy = value; OnPropertyChanged();
    }
}

Step 3: To inject the viewmodel using dependency injection, you must declare the ViewModel class in the project’s startup.cs file and should register it as Scoped. For future improvements, we will also extract the interface of the viewmodel in order to use it for injection. Refer to the following code.

services.AddScoped<IToDoViewModel, ToDoBasicViewModel>();

Step 4: Now let’s move on to the main page, ToDoPage.razor, and its nested components. First, an instance of the viewmodel is injected.

Then, in order to successfully update the entire view, the page and its components, within the OnInitializedAsync method, we have to subscribe the PropertyChanged event and invoke the Blazor StateHasChanged method. Refer to the following code example.

protected override async Task OnInitializedAsync()
{
    ViewModel.PropertyChanged += async (sender, e) => { 
        await InvokeAsync(() =>
        {
            StateHasChanged();
        });
    };
    await base.OnInitializedAsync();
}

Step 5: To avoid memory leaks, we have implemented the IDisposable interface as shown in the following code.

public void Dispose()
{
    ViewModel.PropertyChanged -= OnPropertyChangedHandler;
}

Step 6: Now, by taking advantage of CascadingValues, we can pass viewmodel as a parameter to the child components. In this way, they will also receive information about the state change from the parent component. Refer to the following code example.

<CascadingValue Value=@ViewModel Name="ViewModel">
    <ToDoListComponent />
    <ToDoFormComponent />
</CascadingValue>

The components ToDoFormComponent.razor and ToDoListComponent.razor are quite simple. The former implements a form for inserting and editing. The latter exposes the list of to-do items. Both are bound to the viewmodel elements.

Running the app, all works fine: components do their duty by sharing the state through the viewmodel and the state will never be lost even when navigating from one page to another.

Such a small application could have been developed in many other simpler ways. This is only a sample. Try to think of this architecture in applications with a much higher degree of complexity, with many nested components and many more variables: the benefits are many and relevant.

Now, let’s try to further improve our code by making it reusable.

Code re-usability

It would be nice to extrapolate the generic methods of the viewmodel and create a base class from which all viewmodels are inherited.

To do so, please refer to the following code example.

public abstract class BaseViewModel: INotifyPropertyChanged
{
    private bool isBusy = false; 
    public bool IsBusy
    {
        get => isBusy;
        set
        {
            SetValue(ref isBusy, value);
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {

        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    protected void SetValue<T>(ref T backingFiled, T value, [CallerMemberName] string propertyName = null)
    {
    if (EqualityComparer<T>.Default.Equals(backingFiled, value)) return; 
        backingFiled = value;
    OnPropertyChanged(propertyName);
    }
}

As you can see, implementing the SetValue helper has also improved the system as far as how it raises the OnPropertyChanged event.

Our new ToDoFinalViewModel will now be easier. Please refer to the following code example.

public class ToDoFinalViewModel : BaseViewModel, IToDoViewModel
{
    private List<ToDoItem> toDoItemList = new List<ToDoItem>(); 
    public List<ToDoItem> ToDoItemList
    {
        get => toDoItemList; 
        private set
        {
            SetValue(ref toDoItemList, value);
        }
    }

    private ToDoItem toDoItem = new ToDoItem(); 
    public ToDoItem ToDoItem
    {
        get => toDoItem; 
    set
    {
        SetValue(ref toDoItem, value);
    }
}

public int ItemsToDo
{
    get
    {
        return ToDoItemList.Where(i => i.Done.Equals(false)).Count();
    }
}

public void SaveToDoItem(ToDoItem todoitem)
{
    IsBusy = true;
    if (todoitem.Id.Equals(Guid.Empty))
    {
        todoitem.Id = Guid.NewGuid();
    }
    else
    {
        toDoItemList.Remove(todoitem);
    }

    toDoItemList.Add(todoitem);

    OnPropertyChanged(nameof(ToDoItemList)); 
    IsBusy = false;
}

Now, taking advantage of the Dependency Injection system, let’s edit the startup.cs file as like the following code.

services.AddScoped<IToDoViewModel, ToDoFinalViewModel>();

That’s it! No other changes are needed.

Resource

This complete application is available in this GitHub location.

Conclusion

In this post, we saw a smart way to adopt the Model-View-ViewModel pattern in Blazor projects. Blazor doesn’t have any native support for MVVM, but we’ve also shown that it’s pretty easy to use with it. By adopting this pattern, we have solved our state management problems and also improved the organization of our code. In addition, now we can write unit tests to check the behavior of our code.

Syncfusion Essential Studio for Blazor offers over 65 high-performance, lightweight, and responsive UI components for the web, including file-format libraries, in a single package. Please take a look at our live demos in our sample browser, too.

If you are not yet a Syncfusion customer, you can try our 30-day free trial to check out our available features.

If you wish to send us feedback or would like to ask any questions, please feel free to post them in the comments section below, or contact us through our support forum, Direct-Trac, or feedback portal. As always, we would like to learn from you!

Tags:

Share this post:

Share on twitter
Share on facebook
Share on linkedin

Comments (4)

I really love that you’re advocating MVVM. Imo, it is a superior front end set of patterns that could benefit all javascript front-end development frameworks (angular, vue, etc.) as well.

Using event aggregators with this pattern can eliminate the need to use trendy state-store FLUX patterns and frameworks, and really clean up / simplify code. I’ve been pushing for the use of MVVM over some of the newer, trendy FLUX patterns for years now.

I hope that people continue to realize the benefits of MVVM and give it serious consideration in their current or future projects.

Very helpful article. Many thanks!

Interesting article. Great !!!

Some sample how handle with List elements? When I add an element in my ViewModel, the View not refresh. I tried with ObservableCollection but I had not lucky.

Of the top of my head what i might try to get that working is to have base view model (for example ItemsVMBase that derives from a view model base class that is described in actual blog post) for views that handle lists of some type.
That view model would have public property of type ObservableCollection named Items { get; } . Then you could have a component base that has injectable ‘[Inject]’ property or a parameter ‘[Parameter]’ property for viewmodel that derives from that ItemsVMBase “ItemsViewComponentBase where T : ItemsVMBase”.

In the ItemsViewComponentBase OnInitializedAsync() you can then subscribe to Items.CollectionChanged event and in the event handler you can do that “await InvokeAsync(() =>
{
StateHasChanged();
});”

If the injected view model is instantiated to Scoped or Singleton you should also have that dispose method in the base class so the garbage collector can properly collect the view when it is disposed. Also that items property should be instantiated in the constructor of ItemsVMBase or inline in the property declaration “Items { get; } = new ObservableCollection () to avoid null exceptions.

Leave a comment

Popular Now

Be the first to get updates

Subscribe RSS feed
Scroll To Top