Earlier in our previous articles, we created this action to display the student record. In this action, we passed a model to our view by passing an argument to the View method.
- public class StudentsController : Controller
- {
-
- public ActionResult Index()
- {
- var student = new Student
- {
- DOB = new DateTime(1900, 12, 1),
- Name = "Muhammad"
- };
- return View(student);
- }
- }
There are 2 other ways to pass data from action to the views and that’s what I’m going to show you here.
ViewData
One way is to use the ViewDate dictionary. So every controller has a property called ViewDate which is of type ViewDataDictionary. And ViewData is actually the property of base Controller class which we inherit in our custom controller. Another important thing is, ViewData only transfers data from a controller action to view. It didn't work in the opposite direction, and it will be just valid in the current request. When you request an action, you'll see the response in the View and if you redirect on any other page such as it is a different request, now the ViewData variable property will be no more on that request.
Stop here! in the previous articles, I’ve shared an image in which View() is pointing to the Controller’s method. And now I’m telling to you that ViewData is the property of the Controller class.
How can I be sure about it?
Msdn has a classical but detailed documentation in which Microsoft has written all the things including references of the frameworks. What namespace it has, how many classes are there, how many methods and properties there are and how we can use them. Msdn is telling the complete signature details of each property and method to the reader. Here we have Controller base class reference as well.
We know that ViewData is the property of the Controller class, so let’s find it.
Look what we have here. Now, you should explore Controller methods and properties on your own and try to find out these things with their functionalities, what they are, and how they work.
Let’s come back to the point. Now, we’re passing the student record to the view and here, we’re using ViewData.
- public ActionResult Index()
- {
- var student = new Student
- {
- DOB = new DateTime(1900, 12, 1),
- Name = "Muhammad"
- };
- ViewData["std"] = student;
- return View();
- }
Now, let’s go to the Index action view.
Now, instead of using Model property, we need to use ViewData in views to extract our object.
- @model SCMS.Models.Student
-
- @{
- ViewBag.Title = "Index";
- }
-
- <h2>
- @ViewData["std"].
- </h2>
Now look if we press (.) and see intelliSense after @ViewData[“std”], we can’t access the properties of our student class because we already know that ViewData is actually ViewDataDictionary typed,
So if want to work with ViewData as we can do with our class object, then we need to cast it to the student explicitly.
Keep in mind: We must have to cast the ViewData before using it.
Here, we have different techniques of casting.
- @using SCMS.Models
- @model SCMS.Models.Student
-
- @{
- ViewBag.Title = "Index";
- }
-
- <h2>
- @(((Student)ViewData["std"]).Name)
- </h2>
- <h3>
- @(((Student)ViewData["std"]).DOB)
- </h3>
And another approach we can follow here of casting is,
- @using SCMS.Models
- @model SCMS.Models.Student
-
- @{
- ViewBag.Title = "Index";
- var student = ViewData["std"] as Student;
- }
-
- <h2>
- @student.Name
- </h2>
- <h3>
- @student.DOB
- </h3>
These kind of things are all dependent upon the programmer's taste.
Notice how ugly the code iooks. We’ve already used the approach of passing the model class to our views for the sake of passing data to the views in our previous articles. Now decide which approach is better between these two lines
- <h2>@(((Movie)ViewData["Movie"]).Name) </h2>
- @Model.Name
Another problem with this ViewData[“std”] is the magic string
ViewData[“std”]
If we change the magic string in the controller, then we need to keep in mind that we also need to change the ViewData[“”] key value in views as well otherwise we’ll get a null exception.
- public class StudentsController : Controller
- {
-
- public ActionResult Index()
- {
- var student = new Student
- {
- DOB = new DateTime(1900, 12, 1),
- Name = "Muhammad"
- };
- ViewData["std"] = student;
- return View();
- }
- }
So this way of passing data to view is fragile. We have had ViewData in the very first version of MVC and then Microsoft decided to improve this due to its problems and then they introduced ViewBag which is a dynamic type.
ViewBag
ViewBag is nothing but a wrapper around ViewData. Here we’ll use ViewBag with a magic property, we can assign any number of values and properties and even complex types (class objects) to the ViewBag property.
- public class StudentsController : Controller
- {
-
- public ActionResult Index()
- {
- var student = new Student
- {
- DOB = new DateTime(1900, 12, 1),
- Name = "Muhammad"
- };
- ViewBag.std = student;
- return View();
- }
- }
So this ‘std’ property added to the viewbag at runtime which means we don’t get compile time safety.
- @model SCMS.Models.Student
-
- @{
- ViewBag.Title = "Index";
- var student = ViewBag.std;
- }
-
- <h2>
- @student.Name
- </h2>
- <h3>
- @student.DOB
- </h3>
It means if we change the ViewBag magic property in the controller, then we also need to remember to change the property in the view as well. But here we don’t any need to cast the ViewBag magic property because as IntelliSense shows us, ViewBag is of dynamic type and at the runtime dynamic type can automatically work.
ViewModels
In the index view, currently we just display the name of the student but what if we want to display the information of 2 models' classes in a single view like we want to display the information of Student and in which class he/she is studying. So there are 2 different model classes but we already know that we can pass just 1 single object to the view. Then how can we handle this kind of scenario?
And here View Model comes into the picture.
A View Model is a model class specifically built for a view, it includes any data specific to that view. Let’s try to understand the view model practically.
First of all, add the ‘Subject’ class in a models folder and give it a couple of properties.
- public class Subject
- {
- public int Id { get; set; }
- public string SubjectName { get; set; }
- public int TotalPeriods { get; set; }
- }
In our Solution Explorer, Models folder is just for the model classes. It is actually our Data layer. So we’ll put our view model class in a separate folder. So let’s create a new folder ‘ViewModels’ and create the class here ‘StudentsInfoViewModel’ and we use the convention of ViewModels as a suffix in name. This class needs 2 properties
- public class StudentInfoViewModel
- {
- public Student Student { get; set; }
- public List<Subject> Subjects { get; set; }
- }
Now let’s go back to our controller and in the Index action, quickly create a list of subjects.
- public ActionResult Index()
- {
- var student = new Student
- {
- DOB = new DateTime(1900, 12, 1),
- Name = "Muhammad"
- };
-
- var subjects = new List<Subject>
- {
- new Subject { Id = 1, SubjectName = "Urdu", TotalPeriods = 17 },
- new Subject { Id = 2, SubjectName = "Eng", TotalPeriods = 16 },
- new Subject { Id = 3, SubjectName = "Math", TotalPeriods = 15 },
- new Subject { Id = 4, SubjectName = "Islamiyat", TotalPeriods = 14 },
- new Subject { Id = 5, SubjectName = "Science", TotalPeriods = 12 }
- };
-
- var viewModel = new StudentInfoViewModel
- {
- Student = student,
- Subjects = subjects
- };
-
- return View(viewModel);
- }
As we’re passing the viewModel class [bulk of data] from our action to views, so also need to catch the viewModel class in our views as well.
- @model SCMS.ViewModels.StudentInfoViewModel
- @{
- ViewBag.Title = "Index";
- }
And now we’ll also access the list of the subjects as well. We also know how to use the Razor syntax in our views. So it is quite easy for us to render the list in the Views.
- @model SCMS.ViewModels.StudentInfoViewModel
- @{
- ViewBag.Title = "Index";
- }
-
- <h2>@Model.Student.Name</h2>
- <h3>Subjects</h3>
- <ul>
- @foreach (var subject in Model.Subjects)
- {
- <li>@subject.SubjectName: @subject.TotalPeriods periods</li>
- }
- </ul>
Now build and run the application. Here is the output of our program
Conclusion
The purpose of this article is to share the different techniques through which we can pass the data from our controller action to the views and how we can manage and use the ViewData, ViewBag and ViewModels.
There are different issues and possible exceptions if we use the ViewBag and ViewData in our application. But if we use the view models in our program then we can perform our tasks very easily and with a strongly typed feature. Strongly typed feature enables the IntelliSense for us. We become aware of any kind of problem at compile time.
We should only go with the ViewBag or ViewData when we really need them, otherwise, the best programming practice is to avoid them.