Make Partial on PageBase and PageModel work correctly (#13013)
* Make Partial on PageBase and PageModel work correctly Fixes https://github.com/aspnet/AspNetCore/issues/10438
This commit is contained in:
parent
c80f7d1dd9
commit
9bd027aa99
|
|
@ -368,6 +368,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
{
|
||||
protected PageBase() { }
|
||||
public Microsoft.AspNetCore.Http.HttpContext HttpContext { get { throw null; } }
|
||||
public Microsoft.AspNetCore.Mvc.ModelBinding.IModelMetadataProvider MetadataProvider { get { throw null; } set { } }
|
||||
public Microsoft.AspNetCore.Mvc.ModelBinding.ModelStateDictionary ModelState { get { throw null; } }
|
||||
public Microsoft.AspNetCore.Mvc.RazorPages.PageContext PageContext { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||
public Microsoft.AspNetCore.Http.HttpRequest Request { get { throw null; } }
|
||||
|
|
@ -500,6 +501,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
{
|
||||
protected PageModel() { }
|
||||
public Microsoft.AspNetCore.Http.HttpContext HttpContext { get { throw null; } }
|
||||
public Microsoft.AspNetCore.Mvc.ModelBinding.IModelMetadataProvider MetadataProvider { get { throw null; } set { } }
|
||||
public Microsoft.AspNetCore.Mvc.ModelBinding.ModelStateDictionary ModelState { get { throw null; } }
|
||||
[Microsoft.AspNetCore.Mvc.RazorPages.PageContextAttribute]
|
||||
public Microsoft.AspNetCore.Mvc.RazorPages.PageContext PageContext { get { throw null; } set { } }
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding;
|
|||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
|
@ -61,6 +62,19 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
/// </summary>
|
||||
public ModelStateDictionary ModelState => PageContext?.ModelState;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IModelMetadataProvider"/>.
|
||||
/// </summary>
|
||||
public IModelMetadataProvider MetadataProvider
|
||||
{
|
||||
get
|
||||
{
|
||||
_metadataProvider ??= HttpContext?.RequestServices?.GetRequiredService<IModelMetadataProvider>();
|
||||
return _metadataProvider;
|
||||
}
|
||||
set => _metadataProvider = value ?? throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
private IObjectModelValidator ObjectValidator
|
||||
{
|
||||
get
|
||||
|
|
@ -74,19 +88,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
}
|
||||
}
|
||||
|
||||
private IModelMetadataProvider MetadataProvider
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_metadataProvider == null)
|
||||
{
|
||||
_metadataProvider = HttpContext?.RequestServices?.GetRequiredService<IModelMetadataProvider>();
|
||||
}
|
||||
|
||||
return _metadataProvider;
|
||||
}
|
||||
}
|
||||
|
||||
private IModelBinderFactory ModelBinderFactory
|
||||
{
|
||||
get
|
||||
|
|
@ -1206,12 +1207,17 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
/// <returns>The created <see cref="PartialViewResult"/> object for the response.</returns>
|
||||
public virtual PartialViewResult Partial(string viewName, object model)
|
||||
{
|
||||
ViewContext.ViewData.Model = model;
|
||||
// ViewContext.ViewData is an instance of ViewDataDictionary<MyPageModel>, but we need an instance
|
||||
// of ViewDataDictionary<MyPartialViewModel>.
|
||||
var viewData = new ViewDataDictionary(MetadataProvider, ViewContext.ViewData.ModelState)
|
||||
{
|
||||
Model = model,
|
||||
};
|
||||
|
||||
return new PartialViewResult
|
||||
{
|
||||
ViewName = viewName,
|
||||
ViewData = ViewContext.ViewData
|
||||
ViewData = viewData
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -139,6 +139,19 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IModelMetadataProvider"/>.
|
||||
/// </summary>
|
||||
public IModelMetadataProvider MetadataProvider
|
||||
{
|
||||
get
|
||||
{
|
||||
_metadataProvider ??= HttpContext?.RequestServices?.GetRequiredService<IModelMetadataProvider>();
|
||||
return _metadataProvider;
|
||||
}
|
||||
set => _metadataProvider = value ?? throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ViewDataDictionary"/>.
|
||||
/// </summary>
|
||||
|
|
@ -157,19 +170,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
}
|
||||
}
|
||||
|
||||
private IModelMetadataProvider MetadataProvider
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_metadataProvider == null)
|
||||
{
|
||||
_metadataProvider = HttpContext?.RequestServices?.GetRequiredService<IModelMetadataProvider>();
|
||||
}
|
||||
|
||||
return _metadataProvider;
|
||||
}
|
||||
}
|
||||
|
||||
private IModelBinderFactory ModelBinderFactory
|
||||
{
|
||||
get
|
||||
|
|
@ -1631,12 +1631,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
/// <returns>The created <see cref="PartialViewResult"/> object for the response.</returns>
|
||||
public virtual PartialViewResult Partial(string viewName, object model)
|
||||
{
|
||||
ViewData.Model = model;
|
||||
// PageModel.ViewData is an instance of ViewDataDictionary<MyPageModel>, but we need an instance
|
||||
// of ViewDataDictionary<MyPartialViewModel>.
|
||||
|
||||
var viewData = new ViewDataDictionary(MetadataProvider, ViewData.ModelState)
|
||||
{
|
||||
Model = model,
|
||||
};
|
||||
|
||||
return new PartialViewResult
|
||||
{
|
||||
ViewName = viewName,
|
||||
ViewData = ViewData
|
||||
ViewData = viewData
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1898,13 +1898,15 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
public void PartialView_WithName()
|
||||
{
|
||||
// Arrange
|
||||
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary());
|
||||
var modelMetadataProvider = new EmptyModelMetadataProvider();
|
||||
var viewData = new ViewDataDictionary(modelMetadataProvider, new ModelStateDictionary());
|
||||
var pageModel = new TestPageModel
|
||||
{
|
||||
PageContext = new PageContext
|
||||
{
|
||||
ViewData = viewData
|
||||
}
|
||||
},
|
||||
MetadataProvider = modelMetadataProvider,
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -1913,20 +1915,22 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal("LoginStatus", result.ViewName);
|
||||
Assert.Same(viewData, result.ViewData);
|
||||
Assert.Null(result.Model);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PartialView_WithNameAndModel()
|
||||
{
|
||||
// Arrange
|
||||
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary());
|
||||
var modelMetadataProvider = new EmptyModelMetadataProvider();
|
||||
var viewData = new ViewDataDictionary(modelMetadataProvider, new ModelStateDictionary());
|
||||
var pageModel = new TestPageModel
|
||||
{
|
||||
PageContext = new PageContext
|
||||
{
|
||||
ViewData = viewData
|
||||
}
|
||||
},
|
||||
MetadataProvider = modelMetadataProvider,
|
||||
};
|
||||
var model = new { Username = "Admin" };
|
||||
|
||||
|
|
@ -1937,7 +1941,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
Assert.NotNull(result);
|
||||
Assert.Equal("LoginStatus", result.ViewName);
|
||||
Assert.Equal(model, result.Model);
|
||||
Assert.Same(viewData, result.ViewData);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -1700,14 +1700,17 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
public void PartialView_WithName()
|
||||
{
|
||||
// Arrange
|
||||
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary());
|
||||
var modelMetadataProvider = new EmptyModelMetadataProvider();
|
||||
var viewData = new ViewDataDictionary(modelMetadataProvider, new ModelStateDictionary());
|
||||
var pageModel = new TestPage
|
||||
{
|
||||
ViewContext = new ViewContext
|
||||
{
|
||||
ViewData = viewData
|
||||
}
|
||||
},
|
||||
MetadataProvider = modelMetadataProvider,
|
||||
};
|
||||
viewData.Model = pageModel;
|
||||
|
||||
// Act
|
||||
var result = pageModel.Partial("LoginStatus");
|
||||
|
|
@ -1715,21 +1718,24 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal("LoginStatus", result.ViewName);
|
||||
Assert.Same(viewData, result.ViewData);
|
||||
Assert.Null(result.Model);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PartialView_WithNameAndModel()
|
||||
{
|
||||
// Arrange
|
||||
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary());
|
||||
var modelMetadataProvider = new EmptyModelMetadataProvider();
|
||||
var viewData = new ViewDataDictionary(modelMetadataProvider, new ModelStateDictionary());
|
||||
var pageModel = new TestPage
|
||||
{
|
||||
ViewContext = new ViewContext
|
||||
{
|
||||
ViewData = viewData
|
||||
}
|
||||
},
|
||||
MetadataProvider = modelMetadataProvider,
|
||||
};
|
||||
viewData.Model = pageModel;
|
||||
var model = new { Username = "Admin" };
|
||||
|
||||
// Act
|
||||
|
|
@ -1739,7 +1745,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
Assert.NotNull(result);
|
||||
Assert.Equal("LoginStatus", result.ViewName);
|
||||
Assert.Equal(model, result.Model);
|
||||
Assert.Same(viewData, result.ViewData);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
@page
|
||||
@model TestModel
|
||||
@model PagesHome
|
||||
@{
|
||||
|
||||
ViewData["Title"] = "Hello from pages";
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
|
|||
|
||||
namespace MvcSandbox
|
||||
{
|
||||
public class TestModel : PageModel
|
||||
public class PagesHome : PageModel
|
||||
{
|
||||
public string Name { get; private set; } = "World";
|
||||
|
||||
|
|
@ -144,23 +144,43 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Page_Handler_ReturnPartialWithoutModel()
|
||||
public async Task PageWithoutModel_ReturnPartial()
|
||||
{
|
||||
// Act
|
||||
var document = await Client.GetHtmlDocumentAsync("RenderPartialWithoutModel");
|
||||
using var document = await Client.GetHtmlDocumentAsync("PageWithoutModelRenderPartial");
|
||||
|
||||
var element = document.RequiredQuerySelector("#content");
|
||||
Assert.Equal("Welcome, Guest", element.TextContent);
|
||||
Assert.Equal("Hello from Razor Page", element.TextContent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Page_Handler_ReturnPartialWithModel()
|
||||
public async Task PageWithModel_Works()
|
||||
{
|
||||
// Act
|
||||
var document = await Client.GetHtmlDocumentAsync("RenderPartialWithModel");
|
||||
using var document = await Client.GetHtmlDocumentAsync("RenderPartial");
|
||||
|
||||
var element = document.RequiredQuerySelector("#content");
|
||||
Assert.Equal("Welcome, Admin", element.TextContent);
|
||||
Assert.Equal("Hello from RenderPartialModel", element.TextContent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PageWithModel_PartialUsingPageModelWorks()
|
||||
{
|
||||
// Act
|
||||
using var document = await Client.GetHtmlDocumentAsync("RenderPartial/UsePageModelAsPartialModel");
|
||||
|
||||
var element = document.RequiredQuerySelector("#content");
|
||||
Assert.Equal("Hello from RenderPartialWithModel", element.TextContent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PageWithModel_PartialWithNoModel()
|
||||
{
|
||||
// Act
|
||||
using var document = await Client.GetHtmlDocumentAsync("RenderPartial/NoPartialModel");
|
||||
|
||||
var element = document.RequiredQuerySelector("#content");
|
||||
Assert.Equal("Hello default", element.TextContent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace RazorPagesWebSite.Models
|
||||
{
|
||||
public class RenderPartialModel
|
||||
{
|
||||
public string Value { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
@page
|
||||
@using RazorPagesWebSite.Models
|
||||
|
||||
@functions {
|
||||
public IActionResult OnGet() => Partial("_RenderPartial", new RenderPartialModel { Value = "Hello from Razor Page" });
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
@page "{handler?}"
|
||||
@model RazorPagesWebSite.RenderPartialWithModel
|
||||
|
||||
@{
|
||||
throw new Exception("This should not be called");
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using RazorPagesWebSite.Models;
|
||||
|
||||
namespace RazorPagesWebSite
|
||||
{
|
||||
public class RenderPartialWithModel : PageModel
|
||||
{
|
||||
public string Text { get; set; } = $"Hello from {nameof(RenderPartialWithModel)}";
|
||||
|
||||
public IActionResult OnGet() => Partial("_RenderPartial", new RenderPartialModel { Value = $"Hello from {nameof(RenderPartialModel)}" });
|
||||
|
||||
public IActionResult OnGetUsePageModelAsPartialModel() => Partial("_RenderPartialPageModel", this);
|
||||
|
||||
public IActionResult OnGetNoPartialModel() => Partial("_RenderPartial");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace RazorPagesWebSite
|
||||
{
|
||||
public class RenderPartialWithModel : PageModel
|
||||
{
|
||||
public IActionResult OnGet() => Partial("_PartialWithModel", this);
|
||||
|
||||
public string Username => "Admin";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
@page
|
||||
@model RazorPagesWebSite.RenderPartialWithModel
|
||||
|
||||
<p>The partial will be loaded here ...</p>
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
@page
|
||||
|
||||
@functions {
|
||||
public IActionResult OnGet() => Partial("_PartialWithoutModel");
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
<span id="content">Welcome, Guest</span>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
@using RazorPagesWebSite.Models
|
||||
@model RenderPartialModel
|
||||
|
||||
<span id="content">@(Model?.Value ?? "Hello default")</span>
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
@model RazorPagesWebSite.RenderPartialWithModel
|
||||
|
||||
<span id="content">Welcome, @Model.Username</span>
|
||||
<span id="content">@Model.Text</span>
|
||||
Loading…
Reference in New Issue