ViewModels and Passing Data Between Views
Now that we have an understanding for what a model is, we can focus on how to effectively pass information between the three elements of MVC applications. With our current MVC application, we can add new events and remove events. However, our application is also susceptible to run-time errors. Our view can accept any type of input and if we mistype something in our view, we can run into issues later down the line.
A ViewModel is a model class designed specifically to be used in a view. By utilizing ViewModels in our application, we can make our views strongly-typed and add validation to forms to prevent bad user input. Also, if we have information we want to collect as part of a form, but not save as part of a model, we can store that data as a property of a ViewModel. An example of this would be if we have a form for users to create an account on our site. The form includes two fields: one for the password and one for confirming the new user’s password. While we only want to save the password and may only have a Password property in our model, we can add a ConfirmPassword property to the ViewModel so we can check that the two match before saving the user’s info. These benefits of ViewModels will help reduce potential errors in our application.
Refactoring the Project
To start with understanding why we may want to use a ViewModel, let’s refactor our code to use a model directly in our view. This will require some updates to our controllers and views.
We need to do the following steps:
- Create our new ViewModel
- Update the Model
- Update our Index Action Method
- Update the Index View
- Update the Add and New Event Action Methods
- Update the Add View Model
Let’s get started!
Creating a ViewModel
Add a
ViewModelsdirectory at the top level of the project.Add a new class to the
ViewModelsdirectory and name itAddEventViewModel.

Add
NameandDescriptionproperties to the new class. We can remove the constructor. We do not need it at this time.We will need to declare the
NameandDescriptionas nullable. You do this by using the nullable value type?after the modifiers. To see these in action, look at lines 7 and 8 in the code block below.Check Your Code3 4 5 6 7 8 9 10namespace CodingEvents.ViewModels { public class AddEventViewModel { public string? Name { get; set; } public string? Description { get; set; } } }NoteDeclaring a Value Nullable
Do you see the
?afterstringin lines 7 and 8 in the code block above? This declares this property to be a nullable value type. This means that the value ofNameorDescriptionis allowed to be null at some point.A null value for either of these fields is not ideal, but we need them to have this flexibility when we begin to add validation attributes later in this chapter. If the validation checks fail, a new event will not be added to the project’s data storage.
Check out these resources on nullable value types and strategies to handle them.
We will declare our
Eventfields as nullable types, too. See the next section below.
Update the Model with Nullable Values
We need to declare the fields of our Event model nullable as well.
| |
Update the Index Action Method in the Controller
In the EventsController, find the Index action method. We want to convert our ViewBag to a List collection type.
Update the
ViewBag.eventsto aListofEventobjects. Let’s store this list in a variable calledevents.Pass the contents of the data layer to the new
List.Return the new list
eventsto the View.Check Your Code19 20 21 22 23 24 25// GET: /<controller>/ public IActionResult Index() { List<Event> events = new List<Event>(EventData.GetAll()); return View(events); }
Update the Index View
Now that we are storing our items in a List, we need to import the model into our Events/Index.cshtml view so we can use the new events collection. We can start by adding a @using statement to let the view know which model to reference. We can also use the @model statement to let the view know which type of object to expect. In this case we can expect a list of Event objects.
Add a
@usingstatement that informs the view about which portion of the project to access.Add a
@modelstatement to inform the view about the object type.Check Your Code1 2@using CodingEvents.Models @model List<Event>Wherever we used our
ViewBagproperty, we can now useModelsyntax. We want to count the number ofModelobjects, like we did with theViewBag.Check Your Code14@if (Model.Count == 0)We need to update the loop to check the
Modelfor events (evt).Check Your Code32@foreach (var evt in Model)
Once the view has been updated, run the application!
Update the Add and NewEvent Action Methods in the Controller
In the
EventsController, add ausingstatement for your new ViewModel.Check Your Code8using CodingEvents.ViewModels;In the
Add()action method responsible for retrieving the form to add events, inEventsController, create a new instance ofAddEventViewModelcalledaddEventViewModeland pass it to theView().Check Your Code24 25 26 27 28 29 30[HttpGet] public IActionResult Add() { AddEventViewModel addEventViewModel = new AddEventViewModel(); return View(addEventViewModel); }
Next let’s update our NewEvent action method.
Rename this method
Add()and keep the parameters.Remove the
[Route]attribute, but make sure to keep the[HttpPost]attribute.It should take an instance of
AddEventViewModelcalledaddEventViewModelas a parameter.Check Your Code29 30[HttpPost] public IActionResult Add(AddEventViewModel addEventViewModel)We want to create new events in this action method that we add to our data storage.
We are going to use new constructor syntax to create our
addEventViewModelobjects.- We will create a new
Eventobject callednewEvent. - We will instantiate all of the properties of Event using the
AddEventViewModelinside curly braces{ }. - We want to add our newEvent to our
EventData. - Finally, we want to return a redirected view back to the
/Eventsview to see our newly added event.
Check Your Code29 30 31 32 33 34 35 36 37 38 39 40 41[HttpPost] public IActionResult Add(AddEventViewModel addEventViewModel) { Event newEvent = new Event { Name = addEventViewModel.Name, Description = addEventViewModel.Description }; EventData.Add(newEvent); return Redirect("/Events"); }- We will create a new
You should now have two Add() methods. The framework is clever enough to know the difference. The [HttpPost] attribute designates the Add method that processes the form while the other Add() method retrieves the form.
This is similar to how we created the Delete() action methods in the previous chapter
.
This renaming is not critical to your application, but can help you with the design logic of the program as it grows in size.
Update the Add View
We are now ready to update our Add view.
Import the ViewModel to the
Add.cshtmlview with the necessary@usingsyntax. Inform the view that we will be using the new ViewModel with the correct@modelsyntax.Check Your Code1 2@using CodingEvents.ViewModels @model AddEventViewModelWe are going to add anchor tags helpers to the form.
Add
asp-controller = Eventsandasp-action = Addto the<form>tag to designate which method the form data should be sent to. We want our form to send data to theAddmethod that handlesPOSTrequests inside theEventscontroller.Add
asp-forto<label>and<input>tags. This allows us to specify which form field corresponds to which property in our ViewModel.Check Your Code6 7 8 9 10 11 12 13 14 15 16<form asp-controller="Events" asp-action="Add" method="post"> <div class="form-group"> <label asp-for="Name"></label> <input asp-for="Name" /> </div> <div class="form-group"> <label asp-for="Description"></label> <input asp-for="Description" /> </div> <input type="submit" value="Add Event" /> </form>WarningIn the code block above, notice the lines where we use
asp-for. We removed thenameandtyperequirements from the<input>tag and addedasp-forto the<label>tag too.asp-forreflects the name we provided to the property in the ViewModel. The reflected name will be used in the view when we run this project.Run your application.
- Test what happens when you provide input in each box.
- Test what happens when you leave one empty.
- Finally, test what happens when you leave all of them blank. Looks like the app would benefit from some validation.
Recap for the Refactor
This was merely a refactor so the functionality of the app hasn’t changed, but we have eliminated some of the possibility of bugs in our code being discovered at runtime! Using a model in a view like this makes our view strongly-typed. Before if we misspelled a property of Event or ViewBag, those errors would have been caught at run-time and possibly interfered with users’ experience. With a strongly-typed view, the same errors would be caught at compile-time before users see the application. Strongly-typed views also support intellisense, so as we work with properties of a model, we can make sure we have the correct property name.
While the functionality of the application remains the same, we are now in a position to easily add validation to our application.
Check Your Understanding
True or False: ViewModels are views designed to specifically be used in models.