尝试在局部视图上使用ViewModel

时间:2018-02-26 18:36:06

标签: c# asp.net-core-mvc

所以我正在使用身份验证模板编辑默认的ASP.NET MVC,我正在尝试为_PartialLogin.cshtml创建一个ViewModel,以显示名称而不是用户的电子邮件。我搜索了很多东西,但它们已经过时了,所以我希望有人可以提供帮助。我甚至不知道创建ViewModel是否是最佳选择。

这是LoginPartialViewModel

namespace AplicacaoRoles2.Models.SharedViewModels
{
    public class PartialLoginViewModel
    {
        [Required]
        public string Nome { get; set; }
    }
}

_LoginPartial.cshtml

@using Microsoft.AspNetCore.Identity
@using AplicacaoRoles2.Models

@model AplicacaoRoles2.Models.SharedViewModels.PartialLoginViewModel

@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager

@if (SignInManager.IsSignedIn(User))
{
    <form asp-area="" asp-controller="Account" asp-action="Logout" method="post" id="logoutForm" class="navbar-right">
        <ul class="nav navbar-nav navbar-right">
            <li>
                <a asp-area="" asp-controller="Manage" asp-action="Index" title="Manage">Hello @Html.DisplayFor(model => model.Nome) !</a>
            </li>

(...) HomeController

public class HomeController : Controller
{
    private readonly ApplicationDbContext _context;

    public HomeController(ApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<IActionResult> Index()
    {
        PartialLoginViewModel model = new PartialLoginViewModel();
        var userName = User.Identity.Name;
        var applicationUser = await _context.ApplicationUser.SingleOrDefaultAsync(m => m.UserName == userName);

        model.Nome = applicationUser.Nome;

        return View(model);
    }

_Layout.cshtml上,这部分很有用:

@Html.Partial("_LoginPartial")

(简化代码以缩短此内容)

所以当我在Home/index时,这工作正常,因为它不是模型,但是当我进入带有模型的页面时,它会显示模型错误。

我可以替换:

@Html.Partial("_LoginPartial")

@Html.Partial("_LoginPartial", new AplicacaoRoles2.Models.SharedViewModels.PartialLoginViewModel())

但问题是我通过创建新实例而丢失了信息...... 谢谢。

**像'Nome'这样的单词没有拼写错误,它们是葡萄牙语

3 个答案:

答案 0 :(得分:2)

让我们先看看您遇到的问题是什么,然后我们将应用修复程序。

问题/问题

_LoginPartial.cshtmlShared/_Layout.cshtml调用,除非您更改了它;它似乎你没有改变它所以它使用默认值。没关系,你不必改变它。当您访问Home/Index时,您创建了PartialLoginViewModel的实例并将其传递给Home/Index.cshtml。默认情况下,所有.cshtml个文件都使用Shared/_Layout.cshtml,因此基本上这就是PartialLoginViewModel传递的方式:

主页/索引(控制器) =&gt; 主页/ Index.cshtml =&gt; 共享/ _Layout.cshtml      =&gt; _LoginPartial.cshtml

因此,在上文中,很明显_LoginPartial.cshtml最终获得了PartialLoginViewModel,这就是为什么它在您访问Home/Index时有效。一切都很好,花花公子:)

现在让我们看看当你访问另一个页面时会发生什么;说SomeController.SomeAction。让我们进一步想象另一个页面有一个SomeModel类型的模型。以下是SomeModel的传递方式:

部分/ SomeAction(控制器) =&gt; 部分/ SomeAction.cshtml =&gt; 共享/ _Layout.cshtml =&gt; _LoginPartial .cshtml

繁荣失败是因为_LoginPartial.cshtml期待的是一个具有PartialLoginViewModel属性的模型,而是你给它SomeModel

不是修复

这不起作用:

@Html.Partial("_LoginPartial", new AplicacaoRoles2.Models.SharedViewModels.PartialLoginViewModel())

因为您正在向_LoginPartial发送正确的模型(PartialLoginViewModel),但它是一个新实例,并且尚未设置属性。因此,Name(拼写为Nome)将为空。

这也行不通:

@Html.Partial("_LoginPartial", Model)

因为您从主视图发送了不同类型的模型。

<强>修正

解决问题的一种方法是从PartialLoginViewModel继承您的观点(视图模型)的所有模型,以便部分保持高兴。

public class PartialLoginViewModel
{
    [Required]
    public string Nome { get; set; }
}

public class SomeModel : PartialLoginViewModel
{
    // Code ...
}

您还需要在所有控制器操作中填充Name属性。因此,只需创建一个基类并从中继承所有控制器。像这样:

public class CustomController : Controller
{
    protected virtual async Task<TModel> CreateModel<TModel>() where TModel : PartialLoginViewModel, new()
    {
        TModel model = new TModel();
        var userName = User.Identity.Name;
        var applicationUser = await _context.ApplicationUser.SingleOrDefaultAsync(m => m.UserName == userName);

        // Set common properties
        model.Nome = applicationUser.Nome;

        return model;
    }
}

然后从上面的控制器继承所有控制器并始终调用上面控制器的CreateModel方法,以便它可以填充公共属性,如下所示:

public class EveryoneOfYourControllers : CustomController
{
    public async Task<IActionResult> Index()
    {
        var model = await base.CreateModel<SomeModel>();

        // Set other properties on your model

        return View();
    }
}

所有代码都在一个地方。将来,如果您决定将另一个属性添加到PartialLoginViewModel,则只需在一个位置更改代码即可设置其他属性。

现在你知道了这个问题,你可以提出不同的解决方案。

答案 1 :(得分:1)

您需要将ViewModel从View传递到PartialView,因此您也可以将ViewModel作为Index.cshtml的Model包含在内。您已经通过Controller将所需的ViewModel发送到View,因此您只需将该模型传递给Partial的构造函数。

@Html.Partial("_LoginPartial", Model)

这是ASP.NET核心MVC吗?如果是这样,您最好使用ViewComponent。

答案 2 :(得分:0)

选项一:

由于您将PartialLoginViewModel作为Home索引的主要模型传递,因此Home视图的索引还应包含以下此行作为模型:

@model AplicacaoRoles2.Models.SharedViewModels.PartialLoginViewModel

如果是这种情况,您只需将页面的“模型”对象传递到Home.cshtml视图中的任何视图中:

@Html.Partial("_LoginPartial", Model)

这里的模型应该代表从HomeController.cs传入的模型,包含用户的nome。

选项二

另一种选择是使用ViewData。

public class HomeController : Controller
{
    private readonly ApplicationDbContext _context;

    public HomeController(ApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<IActionResult> Index()
    {
        PartialLoginViewModel model = new PartialLoginViewModel();
        var userName = User.Identity.Name;
        var applicationUser = await _context.ApplicationUser.SingleOrDefaultAsync(m => m.UserName == userName);

        model.Nome = applicationUser.Nome;
        ViewData["userModel"] = model;

        return View();
    }

并使用此行导入模型:

@Html.Partial("_LoginPartial", ViewData["userModel"])