如何在另一个页面中展示一个页面?

How to feature a page in another page?

在我的主页(索引)页面上,如果用户未登录,我希望登录表单直接位于我的主页中,但如果用户已经登录,则将显示索引页面。如果我使用 _LoginPartial 此页面按预期加载,但对于文件夹中的其他页面(例如帐户)我会收到错误消息

CSHTML:

@if (SignInManager.IsSignedIn(User))
{
    <p> Welcome to my website</p>
}
else
{
    @await Html.PartialAsync("Account/Login")
}

`InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'CS.Pages.Manifest.IndexModel', but this ViewDataDictionary instance requires a model item of type 'CS.Pages.Account.LoginModel'.

登录模型构造函数:

public LoginModel(SignInManager<ApplicationUser> signInManager, ILogger<LoginModel> logger)
{
    _signInManager = signInManager;
    _logger = logger;
}

尝试将 LoginModel 实例传递给局部视图

@if (SignInManager.IsSignedIn(User))
{
    <p> Welcome to my website</p>
}
else
{
    @await Html.PartialAsync("Account/Login", new LoginModel())
}

当您加载部分内容时:

@await Html.PartialAsync("Account/Login")

您从中调用它的视图模型是隐式传递的,就好像您已经这样做了一样:

@await Html.PartialAsync("Account/Login", Model)

因此,您收到了错误。部分需要类型 LoginModel 的模型,但您向它传递的是类型 IndexModel 的模型。解决方案当然是传入一个正确类型的模型,即 LoginModel。但是,您的 LoginModel 必须使用 SignInManager<ApplicationUser>ILogger<LoginModel 类型的参数构造。因此,当您尝试仅通过 new LoginModel().

时收到第二个错误的原因

所有这一切实际上归结为首先是对部分的不当使用。需要这种复杂实例化的部分实际上应该是 view component,这会给你更多的设置灵活性 允许你进行注入,这就是你这里需要。

本质上,您只是创建一个继承自 ViewComponent 的 class 并实现方法 InvokeAsync:

public class LoginViewComponent : ViewComponent
{
    private readonly SignInManager<ApplicationUser> _signInManager;
    private readonly ILogger<LoginModel> _logger;

    public LoginViewComponent(SignInManager<ApplicationUser> signInManager, ILogger<LoginModel> logger)
    {
        _signInManager = signInManager;
        _logger = logger;
    }

    public async Task<IViewComponentResult> InvokeAsync()
    {
        var model = await GetModelAsync().ConfigureAwait(false);
        return View(model);
    }

    public Task<LoginModel> GetModelAsync()
    {
        return Task.FromResult(new LoginModel(_signInManager, _logger));
    }
}

请注意,视图组件的构造函数采用实例化 LoginModel 所需的参数。这允许框架注入这些依赖项。然后,构造函数仅将这些存储在一些私有字段中。

InvokeAsync方法调用GetModelAsync获取LoginModel实例,然后returns一个使用该模型的视图。因为,在这里,没有实际的异步工作需要完成,GetModelAsync 方法只是 returns 一个 Task 从更新一个 LoginModel 的结果创建的。必须这样做才能满足 InvokeAsyncTask-返回性质。如果你真的需要一些异步的东西,你可以简单地在这里等待。

有了这些,您只需创建视图 Views\Shared\Components\Login\Default.cshtml。其中包含您当前正在使用的部分视图的内容。 Login\Default.cshtml 部分是约定俗成的。该目录是视图组件的名称 class,减去 ViewComponent 后缀,Default.cshtml 是它将在那里查找的默认视图。如果你愿意,这可以定制,但真的没有理由。

最后,您只需调用:

@await Component.InvokeAsync("Login");

使用这种方法,所有功能(包括模型的创建和传入)都是独立的,因此可以在单独的视图中任何地方调用,甚至您的布局。