♣ 视频地址:
VIEWMODEL,首先讲下概念:
我们通常有数据库的Model,通常叫Entity Model(使用EF和EF Core都知道),数据库里的字段在Model里都有一个类,里面的属性是一一对应的,比如FirstName,LastName,BirthDate
但是我们Controller想返回一个视图,视图所需要的信息并不是上面三个字段的原始信息,它需要完成的名字Name,需要一个年龄Age而不是出生日期,所以我们给这个View返回的Model最好是Name和Age,那我们就新建一个Model就叫View Model,也可以叫DTO(Data Transfer Object)数据转换的对象
而我们再Controller里所做的就是把左边的Model转化为右边的Model,然后传递给View
我们新给Student类加了一个属性BirthDate,如下图
并且在服务里面把这个属性加上,如下图
然后我们建立ViewModels的文件夹,并且添加一个类再添加三个属性,这个类是给HomeController里Index用的,如下图
接下来我们回到Controller,把vms传给View当参数
public IActionResult Index() { var list = _repository.GetAll(); //Select =>将序列的每个元素投影到新表单中,在这里就是HomeIndexViewModel var vms = list.Select(x => new HomeIndexViewModel { Name = $"{x.FirstName}{x.LastName}", //当前时间减去x.BirthDate,得出来的天数除以365 Age = DateTime.Now.Subtract(x.BirthDate).Days / 365 }); return View(vms); }
然后回到视图,修改下代码后运行,看到效果出来了如下图
接下来我们进一步改进下代码
现在我们传进来是个集合,类型是HomeIndexViewModel,但是这样并不特别正确,最好的方法是这样,我们建一个StudentViewModel把HomeIndexViewModel的属性给它,如下图
然后HomeIndexViewModel里改为下面这样
接着我们回到HomeController,改一下我们的代码
public IActionResult Index() { var list = _repository.GetAll(); //Select =>将序列的每个元素投影到新表单中,StudentViewModel var vms = list.Select(x => new StudentViewModel { Id = x.Id, Name = $"{x.FirstName}{x.LastName}", //当前时间减去x.BirthDate,得出来的天数除以365 Age = DateTime.Now.Subtract(x.BirthDate).Days / 365 }); var vm = new HomeIndexViewModel { Students = vms }; return View(vm); }
然后回到视图里,把上面的集合改为一个类,下面这接用Model.Students即可,运行看到也可以,如下图
下面做第二个需求,我们把名字当做超链接,然后点击名字,转到学生的属性信息页面...
首先我们先写Action,传入一个id,根据id来找我们的学生
public IActionResult Detail(int id) { var student = _repository.GetById(id); return View(student); }
接着,我们去建接口,接口的返回类型为T,如下图
然后我们去实现这个接口,这里我把学生信息提到构造函数里了
public class InMemoryRepository : IRepository{ private readonly List _students; public InMemoryRepository() { _students = new List { new Student { Id = 1, FirstName = "Nick", LastName = "Carter", BirthDate =new DateTime(1980,1,4) }, new Student { Id = 2, FirstName = "Kevin", LastName = "Richardson", BirthDate =new DateTime(1974,6,16) }, new Student { Id = 3, FirstName = "Howie", LastName = "D", BirthDate =new DateTime(1978,12,5) } }; } public IEnumerable GetAll() { return _students; } public Student GetById(int id) { Student st = _students.FirstOrDefault(s => s.Id == id); return st; } }
现在我们建立Detail视图,如下图
我们先使用路由试一下,结果好用,如下图
我们用queryString,查询字符串的方式试一下,结果也好用,如下图
那如果路由里面有id,后面又加上querystring,它显示那个呢?显示路由对应的,如下图
现在有3个学生,那我们在路由里输入5呢,当然是报空指针异常的错啦,因为Model为Null,所以Null.FirstName就会发生空指针异常,如下图
这时候我们应该判断Model是否为Null,可以在View里判断,但最好的方式是在Controller里判断
public IActionResult Detail(int id) { var student = _repository.GetById(id); if (student == null) { //如果student为null,怎定向到HomeController下的Index方法 return RedirectToAction("Index"); } return View(student); }
并且在Index视图里加上超链接,如下图
这时候再试试如果在路由里输入id为5,可以看到重新定向到Index页面了
那上面的超链接呢是比较低级的写法,既然我们用了MVC框架,那么MVC里面肯定有些东西来帮助我们建立超链接
下图就是MVC里的写法,第一个参数是超链接的名字,第二个参数是action的名字,第三个是参数id,如果这样写,那么它就会找视图对应的Controller里的Action
如果你想让它跳转到其他的Controller对应的Action,只需要在后面再加一个Controller的参数即可,如下图
上面看到了两个写法,那么还有一个是ASP.NET Core出现之后的叫Tag Helpers(上面叫做Html Helpers...),我们接下来使用Tag Helpers来操作
使用Tag Helpers之前需要进行引用,首先需要在Views文件夹下面添加_ViewImports.cshtml,如下图
咱们先不管这个文件具体是做什么的,这个View不渲染任何东西,它在这里就相当于提供了一些指令,告诉Razor引擎这些视图应该怎么被渲染,并且这些View应该有哪些功能,有点引用的意思
我们在里面写上下面这一段,意思就是我想把Microsoft.AspNetCore.MVC.TagHelpers这个Assembly里面所有的TagHelper在我们所有的视图里可以使用的话,加上*,就这么写就可以了
接下来我们回到index视图页面,如下图asp-开头的就是ASP.NET Core 里面超链接的Helper,虽然它看起来像Html元素的属性,其实不是
MVC框架Razor引擎,它在服务端看到这个东西的时候(在这里就是超链接),它就会对它进行处理,渲染,在服务器端给它处理掉,然后返回客户就是Html
然后我们在明细页加一个返回,也用Tag Helper,如下图