This is the sixth article in a series I have been writing about one way to implement the Model View ViewModel pattern in client side Blazor. If you would like to start reading from the start of the series the first article is located here:

All of the code for this series is stored in on GitHub. The code that I will use at the start of this article is the ArticleFive branch and the 4.5 release. The code we have at the end of this article will be the ArticleSix and the 4.6 release.

How do components communicate with each other?

The same way that MVVM allows us to break up our ViewModels and Models into multiple small and reusable classes, Blazor allows us to break up our Views into multiple small and reusable components. In this article we are going to discuss how components behave inside of a page and a potential display issue that can be encountered when using components.

We are going to expand on the shopping cart we started building in the previous article. We will add some interactivity to the cart allowing our user to pull up an inventory catalog and add items from that catalog into their cart. We will refactor our cart into a separate component and then add in the inventory catalog as a component as well. After we are done we will have a our parent View containing two child Views that will let us examine the behavior of Blazor components.

We will also take this opportunity to discuss how Blazor decides when to render updates to View. We will go over the StageHasChanged() method and times when you would want to call it manually.

Time to refactor our shopping cart

In order to get to where we want to be for testing component behavior we need to make some changes. We want to have an inventory of products that we can pull from as well as a shopping cart that we are placing these items in. To properly identify the items we are working with we can’t stick with the List<InventoryItem> that we used in the last article so we are changing to a Dictionary<int, InventoryItem> which will give each item a unique identifier. We are going to create two Model classes Inventory_Model.cs to represent our available inventory and ShoppingCart_Model.cs to represent the items currently in our cart. We are sticking with the _Model naming to take advantage of the automatic service registration for models we built back in the fourth article. Here is what our inventory model and interface look like:

To start out with we are just declaring our inventory and using the constructor to set up what items and quantities are available as well as the prices. We have one public method now that lets us check if an item has the requested quantity of an item available.

The shopping cart model is also pretty simple. The code listed below also stores a Dictionary of items that are in the cart and has a method to add an item and a method to remove an item.

Adding these two Models as well as the change to a Dictionary has almost completely rewritten our shopping cart ViewModel. Please take a look at the code below:

We have updated our constructor to inject the two new Model classes. We have also changed adding an item to a cart to use the item Id and call AddItem() in the Model. We have a brand new method UpdateCart() which handles updating the ViewModel with the latest contents of the cart including total quantity and price. Finally we have moved DisplayPrice() out to be an extension method so we can use it anywhere in the namespace and improve the readability of our code bindings in our Views.

Finally we had to update the code behind class for our View to fill our starting cart the way we did in the previous article. The new code for that looks like this:

We now get to simply add them by Id and then call UpdateCart() after we are done.

Adding the First Component

For our shopping cart component we are going to move the contents of our cart out to its own component. Create a new View CartContents.razor in the Components folder. Give it a using statement for the ViewModels namespace and move the entire <table> from our parent Model into the component. For this component we will demonstrate passing in the parent ViewModel to the component so we will need to add a functions{} block that provides a parameter for an IShoppingCartModel . We will also need to make a syntax change in our bindings to use our DisplayPrice() extensions method. With all of this in place our component will look like this:

Our parent View is now almost empty. To make use of the component we just need to add it along with a binding to pass the parent ViewModel down to the component:

After all of these changes we can launch our application and see that our shopping cart display is working again and displaying the sample data that we are expecting.

Before we add another ViewModel

For the inventory catalog components I am going to pass in a child ViewModel from the parent. Since I’ll definitely be creating additional ViewModels in the future I am going to update our Startup.cs to auto register my new ViewModels for me. I am going to add one line of code in ConfigureServices() to grab any classes with a name containing _ViewModel and register those automatically at startup as well. The updated code block now looks like this:

A component to select items from inventory

Now that we can automatically register any new ViewModel lets take advantage of that and create a new class InventoryCatalog_ViewModel.cs in our ViewModels folder. The contents of this class follow the MVVM implementation we have laid out throughout this article series. We will create public properties and methods for anything that this ViewModel needs to expose and then create a public interface that contains all of these public members.

The catalog itself is going to be a table with an onclick method attached to the data rows. When the component is initially displayed the table will not be rendered, only a single button that displays or hides the catalog table. This button calls the parent ViewModel through a delegate and fills or empties the catalog table. Once an item in the table is selected there will be a button to add the selected item to our cart (through a delegate back to the parent ViewModel) and a button to cancel the selection.

Back in the dark ages when I learned to program we would have to first declare definitions for our delegates first then declare the delegates and then assign methods to them. In .Net Framework 2.0 the generic delegate Action<T> was added which allows us to create generic delegates which do not return a value and assign a method to them.

We are going to use this generic interface twice in our ViewModel and pull it up to the interface. You can see below that we have one method that takes no parameters and one that takes an int :

public Action AddItemsButtonClickDelegate { get; set; } public Action<int> AddSelectedItemToCartDelegate { get; set; }

The code for our catalog ViewModel class and interface looks like this:

With the ViewModel created we can put together a component to pass it into. In the Components folder create a new razor View InventoryCatalog.razor . It’s another relatively straightforward View, the only thing of note is that it has two conditional rendering blocks. If there are no items in CatalogItems then the catalog table is not rendered and if there is no SelectedItem then the Add and Cancel buttons are not displayed. The full code of the new component looks like this:

Now that it is created we just need to add it in to our View and ViewModel. We can add a single line to ShoppingCart.razor

<BlazorMVVM.Client.Components.InventoryCatalog ViewModel="@ViewModel.InventoryCatalogViewModel" />

In the ViewModel we will need to add in the usual pieces. We’ll need a private variable, a public property and constructor injection. We are also going to add two new methods AddItemsButtonClick() and CheckInventoryAndAddItemToCart() and assigning them to the delegates in the child ViewModel. Every public member that has been added also needs to be pulled up to the interface. After all of these additions our class and interface now look like this:

If we now run our application and click the Display/Hide button we will see our inventory catalog displayed. Click on one of the items in the catalog and we will see the Add and Cancel buttons.

Components in action

Now that we have both components built and worked into our page it’s time to see the fruits of our labor. Click on any item in our inventory catalog and click the Add to Cart button. When we do this we can look at our cart and see that it was … not updated. What exactly is going on? Is there a bug in our code? Did we find a bug in the latest Blazor preview? The answer is that this unexpected behavior is actually, in fact, expected behavior. To understand why we need to talk a little bit about how Blazor renders updates to Razor components.

Every Razor component (and don’t forget that pages are components too) are compiled into a render tree. The render tree is a compiled C# class that is automatically generated for us from our View. If we look at the generated code for our InventoryCatalog.razor the beginning of the class looks like this:

If you compare the code above to our View it’s pretty easy to match thing up. You can also see where it conditionally decides whether or not to add the rendering of the catalog items table based on the item count.

The process Blazor uses to “magically” update the display when things change consists of several steps. The first step is the most important one. Any time a component handles an event it triggers that component to be rendered. Once rendering has been triggered Blazor then does a diff on the render tree to see what has changed and updates the DOM so that we can see our changes reflected on the screen.

Why did our cart not updated then when we added an item from the catalog? The answer is that our onclick() event was handled by our InventoryCatalor.razor component. The event was handled and the component rendered itself afterwards. The problem for us is that our CartContents.razor component was also updated but it did not get rendered after the update. We can observe how this behavior occurs with a few debugging statements added into our code. In all three Views add the following override method (using the proper name for each View).

protected override void OnAfterRender()

{

base.OnAfterRender();

Console.WriteLine("CartContents View OnAfterRender");

}

With this code in place and using the browser debugging console we can see that all three Views render as soon as we navigate to the page.

Any action we take with the catalog only causes the catalog to render. You can see in the Console window that the Parent View and the Cart View never get redrawn. How do we avoid getting into a situation where we update our ViewModel but the user does not see the changes reflected in the View? There are a number of options.

Option 1: Don’t use components

I feel that this is terrible advice that I would not give to anyone but it would work. If every page is a single component then every handled event would cause the entire page to render.

Option 2: Implement INotifyPropertyChanged

A lot of people will be very familiar with this option. You implement this interface on your ViewModel which actually consists of adding a single event to your class:

public event PropertyChangedEventHandler PropertyChanged;

Then you would go into the setters of your public properties and add extra code to call the event if the handler is not null. For Cart that code looks like this:

PropertyChanged?.Invoke(this,

new PropertyChangedEventArgs(nameof(Cart)));

After that is wired up we just need to add an event handler. In the Oninit() method in ShoppingCart.razor.cs add this line of code:

ViewModel.PropertyChanged += (o, e) => StateHasChanged();

StateHasChanged() is a method that tells the View that it needs to re-render. Much like the life cycle methods it is specific tot he View so it is only available in the functions{} block or in our code behind.

If we now launch our application with INotifyPropertyChanged implemented we see that selecting and adding an item from our catalog now results in our cart actually being updated with the proper items, quantity and pricing.

The only catch with INotifyProperyChanged is that we have to pay attention to what it is actually doing. If someone were familiar with it and looking at Blazor for the first time they may think that updating the Cart in the ViewModel only updates that one binding and makes a single change in the DOM. What is actually happening is that StateHasChanged() is being set on the component and it is running the full render process for itself as well as for all of its children if it has any. This difference might not have any actual affect or be able to cause any bugs but it is good to understand what is happening.

Option 3: Use Delegates

This is not much different from INotifyProperyChanged in how it works but is executed slightly differently. If Blazor only renders components after an event is handled and always renders the complete component and its children why do we need to fill our property setters with event calls? Maybe we should just centralize this into a single delegate that our ViewModel can call whenever necessary.

Once again we are going to use the Action generic delegate. We’ll add a public property in our ShoppingCartViewModel.cs for that delegate

public Action StateHasChangedDelegate { get; set; }

and pull it up to the interface. Now we can assign this delegate in OnInit() in our View just like we had done with the event hander earlier:

ViewModel.StateHasChangedDelegate = StateHasChanged;

Now to put our new delegate into action go to the UpdateCart() method in our ViewModel and add a statement to the end of the method to invoke our delegate if it is not null:

StateHasChangedDelegate?.Invoke();

We now have our example updating properly once again. Whether we use INotifyPropertyChanged or delegates will probably come down to personal preference at this point as functionally they are not much different.

Option 4: Don’t try this at home (or work)

Another option that works but that I am staying away from currently is chaining rendering of Views together. In our current example code I can easily use a delegate method call to have OnAfterRender() in InventoryCatalog.razor call StateHasChanged() for CartContents.razor . This will work and will result in the cart properly updating when I add items from the catalog. If it works why am I recommending to not do it? What happens if the next item I work on is to adjust inventory when items are added to or removed from the cart? To make my update take effect I’ll then add a new delegate to call StateHasChanged() going in the other direction. OnAfterRender() fires after every rendering so if I have delegates going in both directions A will caused B to render which will cause A to render and I now have created my first Blazor infinite loop. Also keep in mind that rendering a parent also renders all child components so a single delegate from child to parent set up this way will also cause an infinite loop.

If Blazor had some additional life cycle methods that we could hook into like an OnBeforeRender or an OnStateHasChanged there may have been a way chain updates between components. It’s still possible that someone will come up with an innovative way to do it or new developments in Blazor will make it possible.

Option 5: EventCallback (Use this one)

In .Net Core 3 Preview 3 EventCallback and EventCallback<T> were added. This new delegate works very similarly to Action except some compiler magic takes place that makes it no longer necessary to call StateHasChanged . Using this feature we can now have a child component trigger the rendertree diff on the parent and have our application work we were originally hoping it would.

To put this in place we will add a new parameter to our InventoryCatalog.razor :

[Parameter]

EventCallback<int> AddSelectedItemToCart { get; set; }

To make use of this we will now pass in a public method to our catalog component from the parent View. Go back to ShoppingCart.razor and update it to pass in the CheckInventoryAndAddItemToCart() method to the component. This method needs to be pulled up to the ViewModel interface as well; previously we accessed it through a delegate but now we will be accessing it directly through the EventCallback .

The final step to make this work is update our component to use the EventCallback instead of the delegate. Go back to InventoryCatalog.razor and update the table as follows:

Our onlick property now uses the InvokeAsync() method of EventCallback to call back to the ViewModel. If we run our application now we can see that adding items from the catalog updates the shopping cart properly without making use of the delegates we created in our ViewModel to call StateHasChanged() . We can now delete the unneeded properties and delegates.

In Conclusion

In this article we refactored and added some functionality to our shopping cart to let us better understand how components and component rendering works in Blazor. We went over when our components actually do and do not refresh our display as well as strategies to make sure that they update when we want them to. Finally we saw EventCallback provide a mechanism for the View to refresh when child components are used without having to implement events or delegates.

If you want to discuss this article or Blazor in general you can find me on Twitter @LouisHendricks or join the community in the aspnet/Blazor room on Gitter.

The next article in this series is now published. The new article goes through building a Wizard using client side Blazor and MVVM.