TOC

This article is currently in the process of being translated into Chinese (~1% done).

Views:

Passing data into Views

一个视图 不需要模型或后端传来的任何其它数据就能够独立存在。但在大部分情况下,你需要在视图中访问由控制器生成的数据。由于你的视图不必知道你的控制器(依据“关注点分离”原则),你的控制器负责为视图生成数据。有两种主要途径来实现视图访问后端数据,一种是你通过定义强类型模型,然后将这个模型的实例传给视图;另一种是使用ViewData或ViewBag 容器,使视图能够访问到数据。现在就让我们讨论一下这两种实现方法。

请注意,本文中有一部分内容和前面的那篇快速上手文章《向你的项目添加一个视图》中的内容重叠了。

使用(视图)模型

由于我们现在学习的这项技术叫做MVC,Model(模型)-View(视图)-Controller(控制器)的英文简称,使用(视图)模型毫无疑问是最简洁,也是最常用的向视图传递数据的方法。我们在本系列文章的后续篇幅中对模型展开详细讨论,不过现在暂时只需要知道——模型本质上就是一个对象而已,通常用于表示真实世界中的一些东西。后面你也还会遇到“View Model (视图模型)”这个概念,我们在后续也会对这个概念进行深入讲解。在这块先简单提一嘴,一个视图模型就是被传入视图的那个模型。因此模型视图可以是你项目中已经存在的东西,类似于User 或是 Product对象。还可以是你专门定义的类,用来在视图中访问和呈现数据。

模型无需是一个复杂的类,理论上,模型可以是string,int或者DateTime这类东西。换句话说,.NET framework中的任何东西都可以被当作模型使用。但在实际操作中,你的模型至少得是包含几个属性的类。举个例子,用来展示产品的模型如下所示:

public class Product
{
public string Title { get; set; }

public double Price { get; set; }
}

The connection between the Model and the View is made by the Controller - it can take input from the user and turn it into relevant data for the View, e.g. like this:

public IActionResult Details(int id)
{
Product product = new Product()
{
Title = "Toilet Paper",
Price = 1.99
};
return View(product);
}

Notice how I pass the product instance to the View when I call the View() method. Inside the Product/Details View, I can now define the Product class as the Model this View can (and should) expect, using the @model directive, located at the top of your View file:

@model HelloMVCWorld.Models.Product

With that in place, you can now start using your (View) Model and its properties in your View:

@model HelloMVCWorld.Models.Product

<h1>@Model.Title</h1>
Price: @Model.Price

Thanks to the fact that you specify a strong type as the Model of the View, you will get help accessing members of the object through IntelliSense and your code will be validated during the Build process, to make sure that you only use properties/methods found on the Model object.

Dynamic Views

If you don't declare a Model type for your View, using the @model directive, the View will not expect any Model of a specific type to be passed into it, but that doesn't prevent you from actually doing just that and even using the object. This is often referred to as a Dynamic View, and is not used very often, because you lose out on the benefits of IntelliSense support and compile-time check of the properties.

So, you could take the example above and remove the @model directive in the top of the View, and it would still work. In this situation, the Model property would be considered a dynamic object, where you can access properties without the compiler checking for their existence. Of course that also means that if you try to access a non-existing property, page generation will fail during runtime, resulting in a nasty exception. Therefore, dynamic views are not used a lot.

Using the ViewData/ViewBag containers

As an alternative to the strongly typed approach to passing data to a View, you can use the so-called ViewData/ViewBag containers. You can add stuff to them from the Controller and then automatically be able to access the stored data in your Views. It's actually quite easy and you can accomplish almost the same things as if you had used a strongly typed Model. Here's an example:

public IActionResult DetailsViewData(int id)
{
ViewBag.ProductTitle = "Toilet Paper";
ViewBag.ProductPrice = 1.99;
return View();
}

You can now access this data in your View just as easily, thanks to the always-available property called ViewBag:

<h1>@ViewBag.ProducTtitle</h1>
Price: @ViewBag.ProductPrice

The major difference here is that there's no compile-time checking of these properties, and no help from the IntelliSense when writing them. In other words, you could easily misspell a property name and you wouldn't notice until you tried using the View. Therefore you should only use the ViewData/ViewBag for very small amounts of data. However, you should remember that you are free to combine the usage of ViewData/ViewBag with a traditional, strongly typed View.

What's the difference between ViewData and ViewBag?

You will have noticed that I have used two terms for dealing with dynamic view data: The ViewData and ViewBag properties. These actually both refer to the same thing, which is a Dictionary of keys and values (the objects). However, the ViewBag is a DynamicObject representation of the Dictionary, allowing you to access the data as if it were properties of the ViewBag object, instead of entries in a Dictionary. You are free to use both properties interchangeably, since they always refer to the same data, so for comparison, here's an example of how it would look if you used both approaches:

Controller:

public IActionResult DetailsViewData(int id)
{
ViewData["ProductTitle"] = "Toilet Paper";
ViewBag.ProductPrice = 1.99;
return View();
}

View:

<h1>@ViewData["ProducTtitle"]</h1>
Price: @ViewBag.ProductPrice

Notice how I use the ViewData and therefore a Dictionary-based approach for reading and writing the ProductTitle, while I use the object-based dot-notation to access the ProductPrice. That leaves you with a few smaller difference, e.g. the ability to check for the presence of a given key/value in the ViewData collection (using the ContainsKey() method). At the end of the day, you should use the approach that you find most appealing and then stick with it for consistency.

Summary

You can pass data from a Controller to a View using several different approaches, but the recommended one is almost always the strongly typed View, where you define the type of Model that your View can expect. This gives you IntelliSense support and compile-time checking of the properties and methods you try to use.


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!