Add partial helper to Razor Page \ PageModel

Fixes #7885
This commit is contained in:
hishamco 2018-07-01 19:52:08 +03:00 committed by Pranav K
parent d2cfbd2671
commit dee479fda7
No known key found for this signature in database
GPG Key ID: 1963DA6D96C3057A
13 changed files with 240 additions and 37 deletions

View File

@ -1214,6 +1214,33 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
public virtual UnauthorizedResult Unauthorized()
=> new UnauthorizedResult();
/// <summary>
/// Creates a <see cref="PartialViewResult"/> by specifying the name of a partial to render.
/// </summary>
/// <param name="viewName">The partial name.</param>
/// <returns>The created <see cref="PartialViewResult"/> object for the response.</returns>
public virtual PartialViewResult Partial(string viewName)
{
return Partial(viewName, model: null);
}
/// <summary>
/// Creates a <see cref="PartialViewResult"/> by specifying the name of a partial to render and the model object.
/// </summary>
/// <param name="viewName">The partial name.</param>
/// <param name="model">The model to be passed into the partial.</param>
/// <returns>The created <see cref="PartialViewResult"/> object for the response.</returns>
public virtual PartialViewResult Partial(string viewName, object model)
{
ViewContext.ViewData.Model = model;
return new PartialViewResult
{
ViewName = viewName,
ViewData = ViewContext.ViewData
};
}
#region ViewComponentResult
/// <summary>
/// Creates a <see cref="ViewComponentResult"/> by specifying the name of a view component to render.

View File

@ -1614,6 +1614,33 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
public virtual UnauthorizedResult Unauthorized()
=> new UnauthorizedResult();
/// <summary>
/// Creates a <see cref="PartialViewResult"/> by specifying the name of a partial to render.
/// </summary>
/// <param name="viewName">The partial name.</param>
/// <returns>The created <see cref="PartialViewResult"/> object for the response.</returns>
public virtual PartialViewResult Partial(string viewName)
{
return Partial(viewName, model: null);
}
/// <summary>
/// Creates a <see cref="PartialViewResult"/> by specifying the name of a partial to render and the model object.
/// </summary>
/// <param name="viewName">The partial name.</param>
/// <param name="model">The model to be passed into the partial.</param>
/// <returns>The created <see cref="PartialViewResult"/> object for the response.</returns>
public virtual PartialViewResult Partial(string viewName, object model)
{
ViewData.Model = model;
return new PartialViewResult
{
ViewName = viewName,
ViewData = ViewData
};
}
#region ViewComponentResult
/// <summary>
/// Creates a <see cref="ViewComponentResult"/> by specifying the name of a view component to render.

View File

@ -1,10 +1,8 @@
// 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 System;
using System.Linq;
using System.Net.Http;
using AngleSharp.Dom.Html;
using AngleSharp.Parser.Html;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
@ -19,27 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
var parser = new HtmlParser();
var htmlDocument = parser.Parse(htmlContent);
return RetrieveAntiforgeryToken(htmlDocument);
}
public static string RetrieveAntiforgeryToken(IHtmlDocument htmlDocument)
{
var hiddenInputs = htmlDocument.QuerySelectorAll("form input[type=hidden]");
foreach (var input in hiddenInputs)
{
if (!input.HasAttribute("name"))
{
continue;
}
var name = input.GetAttribute("name");
if (name == "__RequestVerificationToken" || name == "HtmlEncode[[__RequestVerificationToken]]")
{
return input.GetAttribute("value");
}
}
throw new Exception($"Antiforgery token could not be located in {htmlDocument.Source.Text}.");
return htmlDocument.RetrieveAntiforgeryToken();
}
public static CookieMetadata RetrieveAntiforgeryCookie(HttpResponseMessage response)

View File

@ -566,7 +566,7 @@ Products: Music Systems, Televisions (3)";
var document = await Client.GetHtmlDocumentAsync(url);
// Assert
var banner = QuerySelector(document, ".banner");
var banner = document.RequiredQuerySelector(".banner");
Assert.Equal("Some status message", banner.TextContent);
}
@ -581,21 +581,10 @@ Products: Music Systems, Televisions (3)";
var document = await Client.GetHtmlDocumentAsync(url);
// Assert
var banner = QuerySelector(document, ".banner");
var banner = document.RequiredQuerySelector(".banner");
Assert.Empty(banner.TextContent);
}
private static IElement QuerySelector(IHtmlDocument document, string selector)
{
var element = document.QuerySelector(selector);
if (element == null)
{
throw new ArgumentException($"Document does not contain element that matches the selector {selector}: " + Environment.NewLine + document.DocumentElement.OuterHtml);
}
return element;
}
private static HttpRequestMessage RequestWithLocale(string url, string locale)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);

View File

@ -0,0 +1,43 @@
// 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 System;
using AngleSharp.Dom;
using AngleSharp.Dom.Html;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public static class IHtmlDocumentExtensions
{
public static IElement RequiredQuerySelector(this IHtmlDocument document, string selector)
{
var element = document.QuerySelector(selector);
if (element == null)
{
throw new ArgumentException($"Document does not contain element that matches the selector {selector}: " + Environment.NewLine + document.DocumentElement.OuterHtml);
}
return element;
}
public static string RetrieveAntiforgeryToken(this IHtmlDocument htmlDocument)
{
var hiddenInputs = htmlDocument.QuerySelectorAll("form input[type=hidden]");
foreach (var input in hiddenInputs)
{
if (!input.HasAttribute("name"))
{
continue;
}
var name = input.GetAttribute("name");
if (name == "__RequestVerificationToken" || name == "HtmlEncode[[__RequestVerificationToken]]")
{
return input.GetAttribute("value");
}
}
throw new Exception($"Antiforgery token could not be located in {htmlDocument.Source.Text}.");
}
}
}

View File

@ -11,7 +11,6 @@ using System.Net.Http.Headers;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Testing;
using Newtonsoft.Json.Linq;
@ -144,6 +143,26 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
Assert.Equal("CustomActionResult", content);
}
[Fact]
public async Task Page_Handler_ReturnPartialWithoutModel()
{
// Act
var document = await Client.GetHtmlDocumentAsync("RenderPartialWithoutModel");
var element = document.RequiredQuerySelector("#content");
Assert.Equal("Welcome, Guest", element.TextContent);
}
[Fact]
public async Task Page_Handler_ReturnPartialWithModel()
{
// Act
var document = await Client.GetHtmlDocumentAsync("RenderPartialWithModel");
var element = document.RequiredQuerySelector("#content");
Assert.Equal("Welcome, Admin", element.TextContent);
}
[Fact]
public async Task Page_Handler_AsyncReturnTypeImplementsIActionResult()
{

View File

@ -1894,6 +1894,52 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
testPageModel.Verify();
}
[Fact]
public void PartialView_WithName()
{
// Arrange
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary());
var pageModel = new TestPageModel
{
PageContext = new PageContext
{
ViewData = viewData
}
};
// Act
var result = pageModel.Partial("LoginStatus");
// Assert
Assert.NotNull(result);
Assert.Equal("LoginStatus", result.ViewName);
Assert.Same(viewData, result.ViewData);
}
[Fact]
public void PartialView_WithNameAndModel()
{
// Arrange
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary());
var pageModel = new TestPageModel
{
PageContext = new PageContext
{
ViewData = viewData
}
};
var model = new { Username = "Admin" };
// Act
var result = pageModel.Partial("LoginStatus", model);
// Assert
Assert.NotNull(result);
Assert.Equal("LoginStatus", result.ViewName);
Assert.Equal(model, result.Model);
Assert.Same(viewData, result.ViewData);
}
[Fact]
public void ViewComponent_WithName()
{

View File

@ -1697,6 +1697,52 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
Assert.Equal(statusCode, result.StatusCode);
}
[Fact]
public void PartialView_WithName()
{
// Arrange
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary());
var pageModel = new TestPage
{
ViewContext = new ViewContext
{
ViewData = viewData
}
};
// Act
var result = pageModel.Partial("LoginStatus");
// Assert
Assert.NotNull(result);
Assert.Equal("LoginStatus", result.ViewName);
Assert.Same(viewData, result.ViewData);
}
[Fact]
public void PartialView_WithNameAndModel()
{
// Arrange
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary());
var pageModel = new TestPage
{
ViewContext = new ViewContext
{
ViewData = viewData
}
};
var model = new { Username = "Admin" };
// Act
var result = pageModel.Partial("LoginStatus", model);
// Assert
Assert.NotNull(result);
Assert.Equal("LoginStatus", result.ViewName);
Assert.Equal(model, result.Model);
Assert.Same(viewData, result.ViewData);
}
[Fact]
public void ViewComponent_WithName()
{

View File

@ -0,0 +1,15 @@
// 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";
}
}

View File

@ -0,0 +1,4 @@
@page
@model RazorPagesWebSite.RenderPartialWithModel
<p>The partial will be loaded here ...</p>

View File

@ -0,0 +1,5 @@
@page
@functions {
public IActionResult OnGet() => Partial("_PartialWithoutModel");
}

View File

@ -0,0 +1,3 @@
@model RazorPagesWebSite.RenderPartialWithModel
<span id="content">Welcome, @Model.Username</span>

View File

@ -0,0 +1 @@
<span id="content">Welcome, Guest</span>