This is the third article of a series that details one way to implement the MVVM pattern in client side Blazor. If you would like to start the series from the beginning the first article is located here:

All of the code for this article is available in a GitHub repository. The starting point of this article is contained in the AricleTwo branch as well as the 4.2 release. The finished code at the end of this article is the ArticleThree and the 4.3 release.

New requirements for the application

Last time we added some new features that were sent to us by our fictitious employer. This time we are also going to implement some new requirements sent to us by The Powers That Be. We are going to be adding the following features:

Add a premium account setting to the app

Integrate the new enhanced weather service into the app

Display the current forecast for basic users

Display the enhanced forecast with hourly forecast for premium users

We are going to do all of this while trying to keep our code relatively clean. In our View we are going to add in components to make it more modular. In the ViewModel we are going to add in ViewModel children which will help us stay organized. We are also going to use an IEnumerable collection with dependency injection to help us select the correct implementation of an interface.

Using components in the View

We are actually already using components; any piece of UI in Blazor including a page is a component. The components that we will create now are just pieces of UI that we will add into our page with tags. For the first new component to meet our requirements we are going to move the table in the View out to a separate component.

Make a new folder in the client project and name it Components. In this folder add a new Razor View and name it BasicForecast.razor. Cut everything from the View in the else{} block which starts at <table> and ends at </table> and past it into the new component replacing all of the default content that was in there. To make the component work when used in a parent component we need to add this using statement at the top of the component

And this function block at the bottom of the component

That parameter tells the component that when it is used it will accept an instance of IFetchDataViewModel and make that instance locally available inside of the component as the named object ViewModel. Once this is in place we can replace the three references to fetchDataViewModel that we pasted into the component with ViewModel. The new component now looks like this:

To make use of the component we go back to the View and enter in this one line of code inside of the else{} block where we cut all of the content earlier.

This is telling the View to insert the BasicForecast component in this location on the page and pass in the current instance of our ViewModel into the component. We also need to add an @using statement for the BlazorMVVM.Client.Components folder to the View. Once we have added those lines our updated View looks like this:

If we launch the application now we can see that it works exactly the same as it did when we started. We have now added a little bit of composition to the View.

Refactoring the ViewModel

We now need to make changes to our ViewModel. We are going to need some interactions around premium accounts so we’ll need to add properties and methods. We also should not be passing the entire ViewModel to a child component of the page, the component should only get what it needs. Just like we gave our View a child we are now going to create a child ViewModel.

In the ViewModels folder of the client project add a new C# class named BasicForecastViewModel.cs. We are going to migrate the properties and methods needed for the child component over to this child ViewModel and add a reference to it in the main ViewModel. The new child interface and class now looks like this:

The ViewModel is going to receive an instance of this child from constructor injection so we need to go add it to our Startup.cs. We go back to ConfigureServices() and add what will now be our third entry:

With that in place we can add it into our ViewModel as a new private variable

expose that ViewModel to the View through a new public property

add that property to the ViewModel interface

and update our constructor to inject it and assign it.

We still have a little more cleaning up to do from our refactoring. In RetrieveForecastsAsync() where we assigned a value to _weatherForecasts we need to now assign it to the child ViewModel like this:

We also need to make sure that when we change the temperature scale on the ViewModel that we also change it in the child ViewModel like this:

Once the refactoring is complete our ViewModel interface and class now look like this

With the ViewModel refactored we can now update the View to pass the child ViewModel to the child View. Update the if{} else{} block to do the appropriate checking and pass in the correct parameter. That section of code will now look like this:

Finally we need to update the BasicForecast View so that it accepts a BasicForecastViewModel. We will update the functions block in the child View like this:

Once again again if we now launch the application we will see the forecast appear as it did in the beginning. We have now refactored our View and our ViewModel and our application still works.

Adding Premium accounts

An actual application will have some sort of Identity provider to let it know information about the users. For this article we are going to add another toggle button that lets us swap between Basic and Premium so we can focus on the MVVM pattern.

We are going to implement this toggle pretty much the same way we implemented the temperature display toggle. In FetchDataViewModel we will add another private variable

set that variable to false in the constructor

add a public toggle method

and then pull that method up to the interface.

We want the toggle button to tell us what it is going to do when we click it so we will add a property that will become the button’s text

And pull that up to the interface as well.

Now that we have added all of that we can go back to the parent View and add our new button

If we run the application at this point we will see our button and clicking it will toggle the premium status and the button label.

Integrating the enhanced weather service

For our “enhanced” weather service we are going to call the National Weather Service’s API Web Service. The documentation is located at https://www.weather.gov/documentation/services-web-api. For purposes of not letting this article get too long we are simply going to retrieve the forecast for one location. We will be making changes to FetchDataModel to retrieve the forecast from the API and store the returned data. We will also need a class to deserialize the JSON that is returned by the weather.gov API. To get the class we need we can pull up the endpoint we are going to call in our browser so that we can see the raw JSON that is returned by the API. We can go to https://api.weather.gov/gridpoints/ALY/59,14/forecast in our browser to pull up the raw JSON. We can then copy and paste that JSON over to the form at http://json2csharp.com and click the Generate button. That will generate classes we can use in our application. The interface looks like this:

We will create a new class named WeatherDotGovForecast in the shared project and paste all of that code into the class.

With that class now available we can make the needed changes to the Model. In FetchDataModel add a private variable to hold our new forecast

private WeatherDotGovForecast _realWeatherForecast;

and add a public property to allow access to the data

public WeatherDotGovForecast RealWeatherForecast { get => _realWeatherForecast; private set => _realWeatherForecast = value; }

which we will then pull up to the interface.

WeatherDotGovForecast RealWeatherForecast { get; }

With that in place we can add a new method to our Model (which already has our selected forecast location as part of the call)

If we pull that new method up to the interface

Task RetrieveRealForecastAsync();

we will now be able to call the new weather service. At this point our interface and class for our Model will now look like this:

Giving Premium members the enhanced forecast

To give premium members the enhanced forecast we are going to go into FetchDataViewModel and do some updates to RetrieveForecastsAsync(). First lets grab the foreach loop that fills newForecasts and use Visual Studio Quick Actions to extract it to a separate method. We will call that method PopulateStandardForecastData(). We will also move the call to RetrieveForecastsAsync() into the new method which will require making the new method async as well as using the await keyword to call it.

Now we can make a new private method named PopulateEnchancedForecastData() and code that to have the same behavior except to pull data from from the enhanced forecast. This method will loop through a List<Period> from the new service and use that data to populate our IWeatherForecast[]. It will also convert the Fahrenheit temperate to Celsius to keep our user experience consistent. The code of the new method is below:

The final step is to update RetrieveForecastsAsync() with an if-then block to either call the standard or enhanced forecast based on premium membership. When all of these changes are in place the updated methods look like this:

We now have access to our original forecast as well as the enhanced forecast that we have added to our system. To actually have our display update with the correct forecast for Basic or Premium members we will call RetrieveForecastsAsync() each time we click the button to change the member status. This will require us to change a few of our synchronous calls to asynchronous calls. First we will change TogglePremiumMembership() to async and add the call to to retrieve the correct data to that method

public async Task TogglePremiumMembership()

{

_isPremiumMember = !_isPremiumMember;

await RetrieveForecastsAsync();

}

And change the interface for that method to return a Task.

Task TogglePremiumMembership();

Next we can go to FetchDataView and change the onclick method for our Premium toggle button to make an async call.

onclick=@(async () => await fetchDataViewModel.TogglePremiumMembership())

With these changes in place we can launch our application once more and see our enhanced forecasts.

We can see that there are two entries per day for the enhanced forecast. Looking at the period data we can see that there is a daytime forecast and a nighttime forecast for each day. Let’s go back to the ViewModel and change the enhanced forecast summary code to this:

newForecast.Summary = forecast.name + " - " + forecast.shortForecast;

If we launch the application again the display is more informative for our Premium members.

Hourly forecast for Premium users

The national weather service uses the same JSON format for the hourly forecast as that it does for the daily forecast. This will allow FetchDataModel to retrieve and store it with only a few lines of new code. We will add a private variable to hold the hourly forecast

private WeatherDotGovForecast _hourlyWeatherForecast;

expose it through a public property

public WeatherDotGovForecast HourlyWeatherForecast { get => _hourlyWeatherForecast; private set => _hourlyWeatherForecast = value; }

as well as a method to call and store the data.

public async Task RetrieveHourlyForecastAsync() { _hourlyWeatherForecast = await _http.GetJsonAsync<WeatherDotGovForecast>("https://api.weather.gov/gridpoints/ALY/59,14/forecast/hourly"); }

After that we need to pull up both of those members to the interface.

Task RetrieveHourlyForecastAsync();

WeatherDotGovForecast HourlyWeatherForecast { get; }

With these changes in place the ViewModel now has full access to the hourly forecast through the Model. The interface and class now look like this:

To wrap this feature up without going on too much longer we are going to update the enhanced forecast so that if a premium member clicks on a forecast we will display the hourly forecast for that day. If the member clicks on the forecast again it will switch back to the daily forecast.

To assist the user in selecting a daily forecast we are going to enable hover highlighting in the forecast table. To do this we just update the BasicForecast View’s table definition.

<table class="table table-hover">

Putting the child ViewModel in charge with delegates

Next we are going to wire up the BasicForecast View and BasicForecastViewModel to alert the parent ViewModel when a user clicks on a row in the forecast table. We are going to achieve this by creating a delegate in the BasicForecastViewModel and having the parent ViewModel assign a method to that delegate. If you are unfamiliar with using delegates they are basically a variable that holds a pointer to a method. When you call a delegate you call the method that has been assigned to it. This is a useful way to have a child object pass data to and start an action in a parent object. The first thing we will need to do is define our delegate. Outside of the interface and class we are going to declare the following delegate in our namespace. Notice that the delegate returns a Task because we are going to be doing async calls once again.

public delegate Task ToggleDelegate(DateTime selectedDay);

Using this definition we are going to implement this delegate as public property of the BasicForecastViewModel class

public ToggleDelegate ToggleForecastDelegate { get; set; }

and we will pull that property up to the interface.

ToggleDelegate ToggleForecastDelegate { get; set; }

Now we will declare a method that calls this delegate in the class

public async Task ToggleForecast(DateTime selectedDate)

{

await ToggleForecastDelegate(selectedDate);

}

and pull that method up to the interface as well.

Task ToggleForecast(DateTime selectedDate);

We are not checking if the delegate is null in our method because we will be assigning a method to it as soon as it is created in the constructor of the parent ViewModel. With these changes in place our delegate, interface and class look like this

To call our new delegate we are going to add an onclick event for the rows in the datatable in the child View. The event will pass back the datetime from the selected row. This change to the row definition will make an async call to our new ViewModel method.

<tr onclick="@(async () => await ViewModel.ToggleForecast(forecast.Date))">

We now have the delegate set to be called and have the needed data passed to it. The last step is to assign a method to that delegate. We will go into FetchDataViewModel and create an async method with a matching signature. To test that everything is working as expected we are going to start with just a Console.WriteLine() to see if things work.

private async Task ToggleForecast(DateTime selectedDate)

{

if (_isPremiumMember)

{

Console.WriteLine(selectedDate.ToLongDateString());

}

}

Once we have that in place we can go into the constructor and add a single line of code to assign the new method to the delegate in the child ViewModel.

basicForecastViewModel.ToggleForecastDelegate = ToggleForecast;

If we run the application at this point and hit F12 to open the debugging console we will see that the forecase datatable rows are now highlighted when we hover over them and when we click on the premium version of the forecast (and only the premium version) the selected datetime will print out to the console.

Wrapping up the hourly forecast

We now need to update our ParentViewModel to take the proper actions to toggle between daily and hourly forecast for premium members. Once again we will need to track which state we are in so we will add another private bool to our ViewModel

private bool _isDailyForecast;

and set it to true in the constructor

_isDailyForecast = true;

We need our PopulateEnhancedForecastData() method to now grab either a daily or hourly forecast so we are going to rework that method a bit. First we have to put in a conditional block so that we call the correct endpoint on our API to get the forecast we want. Next we have to conditionally populate our Summary field because the forecast.name is empty on the hourly forecast in the data. This is now our reworked method:

We can now finish up our ViewModel updates by completing the needed changes in ToggleForecast(). We will replace the Console.WriteLine() statement with a few lines of code that toggle the value of _isDailyForecast, create of List<IWeatherForecast> and fill it with the proper forecasts from the API. Finally, we will filter down that list to the entries matching the day we selected if the forecasts are hourly, otherwise grab all the forecasts. That filtering requires a reference to System.Linq so a using statement needs to be added as well. This is the code that we will need to do all of that:

Our final finishing detail for today will be to have the child ViewModel format the date in a way that shows the Date and Time in a meaningful way for our premium users. We will go into BasicForecast one last time and change the <td> element that displays the date to now use this formatting string:

<td>@forecast.Date.ToString("MM/dd/yyyy h:mm tt")</td>

With this final change in place we can launch our application and change from daily to hourly forecasts as a premium member.

Wrapping Up

Today we added some more functionality to our sample project and extended the MVVM architecture we built in the first two articles. The relationship of the objects in our forecast page now look like this:

Out View and ViewModel both have child objects but we have still kept our system cleanly separated. No objects except the ViewModel are aware of our Model and the services it calls and our child View is not even aware of the child ViewModel that it receives its data from. Thanks to the parent ViewModel being responsible for coordinating everything on the page it can pass its child ViewModel over through a parameter in the view. We also were able to have a user action in the child View call a method in the ViewModel with the use of a delegate while once again keeping that child View unaware of the ViewModel.

There are still additional things that we can do to improve our application. There is more data in the National Weather Service API that we have pulled down that we have not presented to the user. We could customize the user experience and make our application architecture even nicer with some additional refactoring. The fourth article in the series can be read at the link below: