parent
41104bff7e
commit
e6c716444d
|
|
@ -15,9 +15,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
||||||
/// <paramref name="context"/>.
|
/// <paramref name="context"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The <see cref="ActionContext"/>.</param>
|
/// <param name="context">The <see cref="ActionContext"/>.</param>
|
||||||
/// <param name="pageName">The name of the page.</param>
|
/// <param name="pageName">The name or path of the page.</param>
|
||||||
/// <returns>The <see cref="RazorPageResult"/> of locating the page.</returns>
|
/// <returns>The <see cref="RazorPageResult"/> of locating the page.</returns>
|
||||||
/// <remarks><seealso cref="IViewEngine.FindView"/>.</remarks>
|
/// <remarks>
|
||||||
|
/// <remarks>Use <see cref="GetPage(string, string)"/> when the absolute or relative
|
||||||
|
/// path of the page is known.</remarks>
|
||||||
|
/// <seealso cref="IViewEngine.FindView"/>.
|
||||||
|
/// </remarks>
|
||||||
RazorPageResult FindPage(ActionContext context, string pageName);
|
RazorPageResult FindPage(ActionContext context, string pageName);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
// 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.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewEngines;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||||
|
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Renders a partial view.
|
||||||
|
/// </summary>
|
||||||
|
[HtmlTargetElement("partial", Attributes = "name", TagStructure = TagStructure.WithoutEndTag)]
|
||||||
|
public class PartialTagHelper : TagHelper
|
||||||
|
{
|
||||||
|
private const string ForAttributeName = "asp-for";
|
||||||
|
private readonly ICompositeViewEngine _viewEngine;
|
||||||
|
private readonly IViewBufferScope _viewBufferScope;
|
||||||
|
|
||||||
|
public PartialTagHelper(
|
||||||
|
ICompositeViewEngine viewEngine,
|
||||||
|
IViewBufferScope viewBufferScope)
|
||||||
|
{
|
||||||
|
_viewEngine = viewEngine ?? throw new ArgumentNullException(nameof(viewEngine));
|
||||||
|
_viewBufferScope = viewBufferScope ?? throw new ArgumentNullException(nameof(viewBufferScope));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name or path of the partial view that is rendered to the response.
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An expression to be evaluated against the current model.
|
||||||
|
/// </summary>
|
||||||
|
[HtmlAttributeName(ForAttributeName)]
|
||||||
|
public ModelExpression For { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="ViewDataDictionary"/> to pass into the partial view.
|
||||||
|
/// </summary>
|
||||||
|
public ViewDataDictionary ViewData { get; set; }
|
||||||
|
|
||||||
|
[HtmlAttributeNotBound]
|
||||||
|
[ViewContext]
|
||||||
|
public ViewContext ViewContext { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
|
||||||
|
{
|
||||||
|
if (context == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
var viewBuffer = new ViewBuffer(_viewBufferScope, Name, ViewBuffer.PartialViewPageSize);
|
||||||
|
using (var writer = new ViewBufferTextWriter(viewBuffer, Encoding.UTF8))
|
||||||
|
{
|
||||||
|
await RenderPartialViewAsync(writer);
|
||||||
|
|
||||||
|
// Reset the TagName. We don't want `partial` to render.
|
||||||
|
output.TagName = null;
|
||||||
|
output.Content.SetHtmlContent(viewBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RenderPartialViewAsync(TextWriter writer)
|
||||||
|
{
|
||||||
|
var viewEngineResult = _viewEngine.GetView(ViewContext.ExecutingFilePath, Name, isMainPage: false);
|
||||||
|
var getViewLocations = viewEngineResult.SearchedLocations;
|
||||||
|
if (!viewEngineResult.Success)
|
||||||
|
{
|
||||||
|
viewEngineResult = _viewEngine.FindView(ViewContext, Name, isMainPage: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!viewEngineResult.Success)
|
||||||
|
{
|
||||||
|
var searchedLocations = Enumerable.Concat(getViewLocations, viewEngineResult.SearchedLocations);
|
||||||
|
var locations = string.Empty;
|
||||||
|
if (searchedLocations.Any())
|
||||||
|
{
|
||||||
|
locations += Environment.NewLine + string.Join(Environment.NewLine, searchedLocations);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
Resources.FormatViewEngine_PartialViewNotFound(Name, locations));
|
||||||
|
}
|
||||||
|
|
||||||
|
var view = viewEngineResult.View;
|
||||||
|
// Determine which ViewData we should use to construct a new ViewData
|
||||||
|
var baseViewData = ViewData ?? ViewContext.ViewData;
|
||||||
|
var model = For?.Model ?? ViewContext.ViewData.Model;
|
||||||
|
var newViewData = new ViewDataDictionary<object>(baseViewData, model);
|
||||||
|
var partialViewContext = new ViewContext(ViewContext, view, newViewData, writer);
|
||||||
|
|
||||||
|
if (For?.Name != null)
|
||||||
|
{
|
||||||
|
newViewData.TemplateInfo.HtmlFieldPrefix = newViewData.TemplateInfo.GetFullHtmlFieldName(For.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
using (view as IDisposable)
|
||||||
|
{
|
||||||
|
await view.RenderAsync(partialViewContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -178,6 +178,20 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
internal static string FormatArgumentCannotContainHtmlSpace()
|
internal static string FormatArgumentCannotContainHtmlSpace()
|
||||||
=> GetString("ArgumentCannotContainHtmlSpace");
|
=> GetString("ArgumentCannotContainHtmlSpace");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The partial view '{0}' was not found. The following locations were searched:{1}
|
||||||
|
/// </summary>
|
||||||
|
internal static string ViewEngine_PartialViewNotFound
|
||||||
|
{
|
||||||
|
get => GetString("ViewEngine_PartialViewNotFound");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The partial view '{0}' was not found. The following locations were searched:{1}
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatViewEngine_PartialViewNotFound(object p0, object p1)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("ViewEngine_PartialViewNotFound"), p0, p1);
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -153,4 +153,7 @@
|
||||||
<data name="ArgumentCannotContainHtmlSpace" xml:space="preserve">
|
<data name="ArgumentCannotContainHtmlSpace" xml:space="preserve">
|
||||||
<value>Value cannot contain HTML space characters.</value>
|
<value>Value cannot contain HTML space characters.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ViewEngine_PartialViewNotFound" xml:space="preserve">
|
||||||
|
<value>The partial view '{0}' was not found. The following locations were searched:{1}</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -108,7 +108,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="ViewResult"/> object by specifying a <paramref name="viewName"/>.
|
/// Creates a <see cref="ViewResult"/> object by specifying a <paramref name="viewName"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="viewName">The name of the view that is rendered to the response.</param>
|
/// <param name="viewName">The name or path of the view that is rendered to the response.</param>
|
||||||
/// <returns>The created <see cref="ViewResult"/> object for the response.</returns>
|
/// <returns>The created <see cref="ViewResult"/> object for the response.</returns>
|
||||||
[NonAction]
|
[NonAction]
|
||||||
public virtual ViewResult View(string viewName)
|
public virtual ViewResult View(string viewName)
|
||||||
|
|
@ -132,7 +132,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// Creates a <see cref="ViewResult"/> object by specifying a <paramref name="viewName"/>
|
/// Creates a <see cref="ViewResult"/> object by specifying a <paramref name="viewName"/>
|
||||||
/// and the <paramref name="model"/> to be rendered by the view.
|
/// and the <paramref name="model"/> to be rendered by the view.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="viewName">The name of the view that is rendered to the response.</param>
|
/// <param name="viewName">The name or path of the view that is rendered to the response.</param>
|
||||||
/// <param name="model">The model that is rendered by the view.</param>
|
/// <param name="model">The model that is rendered by the view.</param>
|
||||||
/// <returns>The created <see cref="ViewResult"/> object for the response.</returns>
|
/// <returns>The created <see cref="ViewResult"/> object for the response.</returns>
|
||||||
[NonAction]
|
[NonAction]
|
||||||
|
|
@ -161,7 +161,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="PartialViewResult"/> object by specifying a <paramref name="viewName"/>.
|
/// Creates a <see cref="PartialViewResult"/> object by specifying a <paramref name="viewName"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="viewName">The name of the view that is rendered to the response.</param>
|
/// <param name="viewName">The name or path of the partial view that is rendered to the response.</param>
|
||||||
/// <returns>The created <see cref="PartialViewResult"/> object for the response.</returns>
|
/// <returns>The created <see cref="PartialViewResult"/> object for the response.</returns>
|
||||||
[NonAction]
|
[NonAction]
|
||||||
public virtual PartialViewResult PartialView(string viewName)
|
public virtual PartialViewResult PartialView(string viewName)
|
||||||
|
|
@ -185,7 +185,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// Creates a <see cref="PartialViewResult"/> object by specifying a <paramref name="viewName"/>
|
/// Creates a <see cref="PartialViewResult"/> object by specifying a <paramref name="viewName"/>
|
||||||
/// and the <paramref name="model"/> to be rendered by the partial view.
|
/// and the <paramref name="model"/> to be rendered by the partial view.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="viewName">The name of the partial view that is rendered to the response.</param>
|
/// <param name="viewName">The name or path of the partial view that is rendered to the response.</param>
|
||||||
/// <param name="model">The model that is rendered by the partial view.</param>
|
/// <param name="model">The model that is rendered by the partial view.</param>
|
||||||
/// <returns>The created <see cref="PartialViewResult"/> object for the response.</returns>
|
/// <returns>The created <see cref="PartialViewResult"/> object for the response.</returns>
|
||||||
[NonAction]
|
[NonAction]
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewEngines;
|
using Microsoft.AspNetCore.Mvc.ViewEngines;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc
|
namespace Microsoft.AspNetCore.Mvc
|
||||||
|
|
@ -23,7 +22,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
public int? StatusCode { get; set; }
|
public int? StatusCode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name of the partial view to render.
|
/// Gets or sets the name or path of the partial view that is rendered to the response.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// When <c>null</c>, defaults to <see cref="ControllerActionDescriptor.ActionName"/>.
|
/// When <c>null</c>, defaults to <see cref="ControllerActionDescriptor.ActionName"/>.
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
||||||
/// <param name="partialViewName">
|
/// <param name="partialViewName">
|
||||||
/// The name of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
/// The name or path of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// A <see cref="Task"/> that on completion returns a new <see cref="IHtmlContent"/> instance containing
|
/// A <see cref="Task"/> that on completion returns a new <see cref="IHtmlContent"/> instance containing
|
||||||
|
|
@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
||||||
/// <param name="partialViewName">
|
/// <param name="partialViewName">
|
||||||
/// The name of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
/// The name or path of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="viewData">A <see cref="ViewDataDictionary"/> to pass into the partial view.</param>
|
/// <param name="viewData">A <see cref="ViewDataDictionary"/> to pass into the partial view.</param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
|
|
@ -76,7 +76,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
||||||
/// <param name="partialViewName">
|
/// <param name="partialViewName">
|
||||||
/// The name of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
/// The name or path of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="model">A model to pass into the partial view.</param>
|
/// <param name="model">A model to pass into the partial view.</param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
|
|
@ -106,7 +106,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
||||||
/// <param name="partialViewName">
|
/// <param name="partialViewName">
|
||||||
/// The name of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
/// The name or path of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// Returns a new <see cref="IHtmlContent"/> instance containing the created HTML.
|
/// Returns a new <see cref="IHtmlContent"/> instance containing the created HTML.
|
||||||
|
|
@ -137,7 +137,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
||||||
/// <param name="partialViewName">
|
/// <param name="partialViewName">
|
||||||
/// The name of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
/// The name or path of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="viewData">A <see cref="ViewDataDictionary"/> to pass into the partial view.</param>
|
/// <param name="viewData">A <see cref="ViewDataDictionary"/> to pass into the partial view.</param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
|
|
@ -170,7 +170,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
||||||
/// <param name="partialViewName">
|
/// <param name="partialViewName">
|
||||||
/// The name of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
/// The name or path of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="model">A model to pass into the partial view.</param>
|
/// <param name="model">A model to pass into the partial view.</param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
|
|
@ -203,7 +203,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
||||||
/// <param name="partialViewName">
|
/// <param name="partialViewName">
|
||||||
/// The name of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
/// The name or path of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="model">A model to pass into the partial view.</param>
|
/// <param name="model">A model to pass into the partial view.</param>
|
||||||
/// <param name="viewData">A <see cref="ViewDataDictionary"/> to pass into the partial view.</param>
|
/// <param name="viewData">A <see cref="ViewDataDictionary"/> to pass into the partial view.</param>
|
||||||
|
|
@ -239,7 +239,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
||||||
/// <param name="partialViewName">
|
/// <param name="partialViewName">
|
||||||
/// The name of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
/// The name or path of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// In this context, "renders" means the method writes its output using <see cref="ViewContext.Writer"/>.
|
/// In this context, "renders" means the method writes its output using <see cref="ViewContext.Writer"/>.
|
||||||
|
|
@ -267,7 +267,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
||||||
/// <param name="partialViewName">
|
/// <param name="partialViewName">
|
||||||
/// The name of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
/// The name or path of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="viewData">A <see cref="ViewDataDictionary"/> to pass into the partial view.</param>
|
/// <param name="viewData">A <see cref="ViewDataDictionary"/> to pass into the partial view.</param>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
|
|
@ -297,7 +297,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
||||||
/// <param name="partialViewName">
|
/// <param name="partialViewName">
|
||||||
/// The name of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
/// The name or path of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="model">A model to pass into the partial view.</param>
|
/// <param name="model">A model to pass into the partial view.</param>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
|
|
@ -327,7 +327,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
||||||
/// <param name="partialViewName">
|
/// <param name="partialViewName">
|
||||||
/// The name of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
/// The name or path of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <returns>A <see cref="Task"/> that renders the created HTML when it executes.</returns>
|
/// <returns>A <see cref="Task"/> that renders the created HTML when it executes.</returns>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
|
|
@ -355,7 +355,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
||||||
/// <param name="partialViewName">
|
/// <param name="partialViewName">
|
||||||
/// The name of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
/// The name or path of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="viewData">A <see cref="ViewDataDictionary"/> to pass into the partial view.</param>
|
/// <param name="viewData">A <see cref="ViewDataDictionary"/> to pass into the partial view.</param>
|
||||||
/// <returns>A <see cref="Task"/> that renders the created HTML when it executes.</returns>
|
/// <returns>A <see cref="Task"/> that renders the created HTML when it executes.</returns>
|
||||||
|
|
@ -385,7 +385,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
/// <param name="htmlHelper">The <see cref="IHtmlHelper"/> instance this method extends.</param>
|
||||||
/// <param name="partialViewName">
|
/// <param name="partialViewName">
|
||||||
/// The name of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
/// The name or path of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="model">A model to pass into the partial view.</param>
|
/// <param name="model">A model to pass into the partial view.</param>
|
||||||
/// <returns>A <see cref="Task"/> that renders the created HTML when it executes.</returns>
|
/// <returns>A <see cref="Task"/> that renders the created HTML when it executes.</returns>
|
||||||
|
|
|
||||||
|
|
@ -570,7 +570,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
/// Renders HTML markup for the specified partial view.
|
/// Renders HTML markup for the specified partial view.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="partialViewName">
|
/// <param name="partialViewName">
|
||||||
/// The name of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
/// The name or path of the partial view used to create the HTML markup. Must not be <c>null</c>.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="model">A model to pass into the partial view.</param>
|
/// <param name="model">A model to pass into the partial view.</param>
|
||||||
/// <param name="viewData">A <see cref="ViewDataDictionary"/> to pass into the partial view.</param>
|
/// <param name="viewData">A <see cref="ViewDataDictionary"/> to pass into the partial view.</param>
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,11 @@ namespace Microsoft.AspNetCore.Mvc.ViewEngines
|
||||||
/// <paramref name="context"/>.
|
/// <paramref name="context"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The <see cref="ActionContext"/>.</param>
|
/// <param name="context">The <see cref="ActionContext"/>.</param>
|
||||||
/// <param name="viewName">The name of the view.</param>
|
/// <param name="viewName">The name or path of the view that is rendered to the response.</param>
|
||||||
/// <param name="isMainPage">Determines if the page being found is the main page for an action.</param>
|
/// <param name="isMainPage">Determines if the page being found is the main page for an action.</param>
|
||||||
/// <returns>The <see cref="ViewEngineResult"/> of locating the view.</returns>
|
/// <returns>The <see cref="ViewEngineResult"/> of locating the view.</returns>
|
||||||
|
/// <remarks>Use <see cref="GetView(string, string, bool)"/> when the absolute or relative
|
||||||
|
/// path of the view is known.</remarks>
|
||||||
ViewEngineResult FindView(ActionContext context, string viewName, bool isMainPage);
|
ViewEngineResult FindView(ActionContext context, string viewName, bool isMainPage);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewEngines;
|
using Microsoft.AspNetCore.Mvc.ViewEngines;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc
|
namespace Microsoft.AspNetCore.Mvc
|
||||||
|
|
@ -23,7 +22,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
public int? StatusCode { get; set; }
|
public int? StatusCode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name of the view to render.
|
/// Gets or sets the name or path of the view that is rendered to the response.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// When <c>null</c>, defaults to <see cref="ControllerActionDescriptor.ActionName"/>.
|
/// When <c>null</c>, defaults to <see cref="ControllerActionDescriptor.ActionName"/>.
|
||||||
|
|
|
||||||
|
|
@ -61,8 +61,12 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
// Only attribute order should differ.
|
// Only attribute order should differ.
|
||||||
{ "Order", "/HtmlGeneration_Order/Submit" },
|
{ "Order", "/HtmlGeneration_Order/Submit" },
|
||||||
{ "OrderUsingHtmlHelpers", "/HtmlGeneration_Order/Submit" },
|
{ "OrderUsingHtmlHelpers", "/HtmlGeneration_Order/Submit" },
|
||||||
|
// Testing PartialTagHelper
|
||||||
|
{ "PartialTagHelperWithoutModel", null },
|
||||||
|
{ "Warehouse", null },
|
||||||
// Testing InputTagHelpers invoked in the partial views
|
// Testing InputTagHelpers invoked in the partial views
|
||||||
{ "ProductList", "/HtmlGeneration_Product" },
|
{ "ProductList", "/HtmlGeneration_Product" },
|
||||||
|
{ "ProductListUsingTagHelpers", "/HtmlGeneration_Product" },
|
||||||
// Testing the ScriptTagHelper
|
// Testing the ScriptTagHelper
|
||||||
{ "Script", null },
|
{ "Script", null },
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
PartialTagHelperWithoutModel: Hello from partial
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form action="/HtmlGeneration_Product" method="post">
|
||||||
|
<div>
|
||||||
|
<label class="product" for="z0__HomePage">HomePage</label>
|
||||||
|
<input type="url" size="50" disabled="disabled" readonly="readonly" id="z0__HomePage" name="[0].HomePage" value="http://www.contoso.com/" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="product" for="z0__Number">Number</label>
|
||||||
|
<input type="number" data-val="true" data-val-required="The Number field is required." id="z0__Number" name="[0].Number" value="0" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="product" for="z0__ProductName">ProductName</label>
|
||||||
|
<input type="text" data-val="true" data-val-required="The ProductName field is required." id="z0__ProductName" name="[0].ProductName" value="Product_0" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="product" for="z0__Description">Description</label>
|
||||||
|
<textarea rows="4" cols="50" class="product" id="z0__Description" name="[0].Description">
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="product" for="z1__HomePage">HomePage</label>
|
||||||
|
<input type="url" size="50" disabled="disabled" readonly="readonly" id="z1__HomePage" name="[1].HomePage" value="" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="product" for="z1__Number">Number</label>
|
||||||
|
<input type="number" data-val="true" data-val-required="The Number field is required." id="z1__Number" name="[1].Number" value="1" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="product" for="z1__ProductName">ProductName</label>
|
||||||
|
<input type="text" data-val="true" data-val-required="The ProductName field is required." id="z1__ProductName" name="[1].ProductName" value="Product_1" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="product" for="z1__Description">Description</label>
|
||||||
|
<textarea rows="4" cols="50" class="product" id="z1__Description" name="[1].Description">
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="product" for="z2__HomePage">HomePage</label>
|
||||||
|
<input type="url" size="50" disabled="disabled" readonly="readonly" id="z2__HomePage" name="[2].HomePage" value="" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="product" for="z2__Number">Number</label>
|
||||||
|
<input type="number" data-val="true" data-val-required="The Number field is required." id="z2__Number" name="[2].Number" value="2" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="product" for="z2__ProductName">ProductName</label>
|
||||||
|
<input type="text" data-val="true" data-val-required="The ProductName field is required." id="z2__ProductName" name="[2].ProductName" value="Product_2" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="product" for="z2__Description">Description</label>
|
||||||
|
<textarea rows="4" cols="50" class="product" id="z2__Description" name="[2].Description">
|
||||||
|
Product_2 description</textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>HtmlFieldPrefix = </div>
|
||||||
|
<input type="submit" />
|
||||||
|
<input name="__RequestVerificationToken" type="hidden" value="{0}" /></form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<h3>City_1</h3>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="Employee_Name">Name</label>
|
||||||
|
<input type="text" id="Employee_Name" name="Employee.Name" value="EmployeeName_1" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="Employee_OfficeNumber">OfficeNumber</label>
|
||||||
|
<input type="number" id="Employee_OfficeNumber" name="Employee.OfficeNumber" value="Number_1" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="Employee_Address">Address</label>
|
||||||
|
<textarea rows="4" cols="50" id="Employee_Address" name="Employee.Address">
|
||||||
|
Address_1</textarea>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,473 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
|
using Microsoft.AspNetCore.Mvc.TestCommon;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewEngines;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||||
|
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Microsoft.Extensions.WebEncoders.Testing;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
|
{
|
||||||
|
public class PartialTagHelperTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessAsync_RendersPartialView_IfGetViewReturnsView()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = "Hello world!";
|
||||||
|
var bufferScope = new TestViewBufferScope();
|
||||||
|
var partialName = "_Partial";
|
||||||
|
var model = new object();
|
||||||
|
var viewContext = GetViewContext();
|
||||||
|
|
||||||
|
var view = new Mock<IView>();
|
||||||
|
view.Setup(v => v.RenderAsync(It.IsAny<ViewContext>()))
|
||||||
|
.Callback((ViewContext v) =>
|
||||||
|
{
|
||||||
|
v.Writer.Write(expected);
|
||||||
|
})
|
||||||
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
|
var viewEngine = new Mock<ICompositeViewEngine>();
|
||||||
|
viewEngine.Setup(v => v.GetView(It.IsAny<string>(), partialName, false))
|
||||||
|
.Returns(ViewEngineResult.Found(partialName, view.Object));
|
||||||
|
|
||||||
|
var tagHelper = new PartialTagHelper(viewEngine.Object, bufferScope)
|
||||||
|
{
|
||||||
|
Name = partialName,
|
||||||
|
ViewContext = viewContext,
|
||||||
|
};
|
||||||
|
var tagHelperContext = GetTagHelperContext();
|
||||||
|
var output = GetTagHelperOutput();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await tagHelper.ProcessAsync(tagHelperContext, output);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var content = HtmlContentUtilities.HtmlContentToString(output.Content, new HtmlTestEncoder());
|
||||||
|
Assert.Equal(expected, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessAsync_RendersPartialView_IfFindViewReturnsView()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = "Hello world!";
|
||||||
|
var bufferScope = new TestViewBufferScope();
|
||||||
|
var partialName = "_Partial";
|
||||||
|
var model = new object();
|
||||||
|
var viewContext = GetViewContext();
|
||||||
|
|
||||||
|
var view = new Mock<IView>();
|
||||||
|
view.Setup(v => v.RenderAsync(It.IsAny<ViewContext>()))
|
||||||
|
.Callback((ViewContext v) =>
|
||||||
|
{
|
||||||
|
v.Writer.Write(expected);
|
||||||
|
})
|
||||||
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
|
var viewEngine = new Mock<ICompositeViewEngine>();
|
||||||
|
viewEngine.Setup(v => v.GetView(It.IsAny<string>(), partialName, false))
|
||||||
|
.Returns(ViewEngineResult.NotFound(partialName, new[] { partialName }));
|
||||||
|
|
||||||
|
viewEngine.Setup(v => v.FindView(viewContext, partialName, false))
|
||||||
|
.Returns(ViewEngineResult.Found(partialName, view.Object));
|
||||||
|
|
||||||
|
var tagHelper = new PartialTagHelper(viewEngine.Object, bufferScope)
|
||||||
|
{
|
||||||
|
Name = partialName,
|
||||||
|
ViewContext = viewContext,
|
||||||
|
};
|
||||||
|
var tagHelperContext = GetTagHelperContext();
|
||||||
|
var output = GetTagHelperOutput();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await tagHelper.ProcessAsync(tagHelperContext, output);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var content = HtmlContentUtilities.HtmlContentToString(output.Content, new HtmlTestEncoder());
|
||||||
|
Assert.Equal(expected, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessAsync_UsesViewDataFromContext()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = "Implicit";
|
||||||
|
var bufferScope = new TestViewBufferScope();
|
||||||
|
var partialName = "_Partial";
|
||||||
|
var viewContext = GetViewContext();
|
||||||
|
viewContext.ViewData["key"] = expected;
|
||||||
|
|
||||||
|
var view = new Mock<IView>();
|
||||||
|
view.Setup(v => v.RenderAsync(It.IsAny<ViewContext>()))
|
||||||
|
.Callback((ViewContext v) =>
|
||||||
|
{
|
||||||
|
v.Writer.Write(v.ViewData["key"]);
|
||||||
|
})
|
||||||
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
|
var viewEngine = new Mock<ICompositeViewEngine>();
|
||||||
|
viewEngine.Setup(v => v.GetView(It.IsAny<string>(), partialName, false))
|
||||||
|
.Returns(ViewEngineResult.NotFound(partialName, new[] { partialName }));
|
||||||
|
|
||||||
|
viewEngine.Setup(v => v.FindView(viewContext, partialName, false))
|
||||||
|
.Returns(ViewEngineResult.Found(partialName, view.Object));
|
||||||
|
|
||||||
|
var tagHelper = new PartialTagHelper(viewEngine.Object, bufferScope)
|
||||||
|
{
|
||||||
|
Name = partialName,
|
||||||
|
ViewContext = viewContext,
|
||||||
|
};
|
||||||
|
var tagHelperContext = GetTagHelperContext();
|
||||||
|
var output = GetTagHelperOutput();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await tagHelper.ProcessAsync(tagHelperContext, output);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var content = HtmlContentUtilities.HtmlContentToString(output.Content, new HtmlTestEncoder());
|
||||||
|
Assert.Equal(expected, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessAsync_UsesPassedInViewData_WhenNotNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = "Explicit";
|
||||||
|
var bufferScope = new TestViewBufferScope();
|
||||||
|
var partialName = "_Partial";
|
||||||
|
var model = new object();
|
||||||
|
var viewData = new ViewDataDictionary(new TestModelMetadataProvider(), new ModelStateDictionary());
|
||||||
|
viewData["key"] = expected;
|
||||||
|
var viewContext = GetViewContext();
|
||||||
|
viewContext.ViewData["key"] = "ViewContext";
|
||||||
|
|
||||||
|
var view = new Mock<IView>();
|
||||||
|
view.Setup(v => v.RenderAsync(It.IsAny<ViewContext>()))
|
||||||
|
.Callback((ViewContext v) =>
|
||||||
|
{
|
||||||
|
v.Writer.Write(v.ViewData["key"]);
|
||||||
|
})
|
||||||
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
|
var viewEngine = new Mock<ICompositeViewEngine>();
|
||||||
|
viewEngine.Setup(v => v.GetView(It.IsAny<string>(), partialName, false))
|
||||||
|
.Returns(ViewEngineResult.Found(partialName, view.Object));
|
||||||
|
|
||||||
|
var tagHelper = new PartialTagHelper(viewEngine.Object, bufferScope)
|
||||||
|
{
|
||||||
|
Name = partialName,
|
||||||
|
ViewContext = viewContext,
|
||||||
|
ViewData = viewData,
|
||||||
|
};
|
||||||
|
var tagHelperContext = GetTagHelperContext();
|
||||||
|
var output = GetTagHelperOutput();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await tagHelper.ProcessAsync(tagHelperContext, output);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var content = HtmlContentUtilities.HtmlContentToString(output.Content, new HtmlTestEncoder());
|
||||||
|
Assert.Equal(expected, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessAsync_UsesModelExpression_ToDetermineModel()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = new PropertyModel();
|
||||||
|
var bufferScope = new TestViewBufferScope();
|
||||||
|
var partialName = "_Partial";
|
||||||
|
var modelMetadataProvider = new TestModelMetadataProvider();
|
||||||
|
var containerModel = new TestModel { Property = expected };
|
||||||
|
var containerModelExplorer = modelMetadataProvider.GetModelExplorerForType(
|
||||||
|
typeof(TestModel),
|
||||||
|
containerModel);
|
||||||
|
var propertyModelExplorer = containerModelExplorer.GetExplorerForProperty(nameof(TestModel.Property));
|
||||||
|
|
||||||
|
var modelExpression = new ModelExpression("Property", propertyModelExplorer);
|
||||||
|
var viewContext = GetViewContext();
|
||||||
|
viewContext.ViewData.Model = new object();
|
||||||
|
|
||||||
|
var view = new Mock<IView>();
|
||||||
|
view.Setup(v => v.RenderAsync(It.IsAny<ViewContext>()))
|
||||||
|
.Callback((ViewContext v) =>
|
||||||
|
{
|
||||||
|
var actual = Assert.IsType<PropertyModel>(v.ViewData.Model);
|
||||||
|
Assert.Same(expected, actual);
|
||||||
|
})
|
||||||
|
.Returns(Task.CompletedTask)
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
var viewEngine = new Mock<ICompositeViewEngine>();
|
||||||
|
viewEngine.Setup(v => v.GetView(It.IsAny<string>(), partialName, false))
|
||||||
|
.Returns(ViewEngineResult.Found(partialName, view.Object));
|
||||||
|
|
||||||
|
var tagHelper = new PartialTagHelper(viewEngine.Object, bufferScope)
|
||||||
|
{
|
||||||
|
Name = partialName,
|
||||||
|
ViewContext = viewContext,
|
||||||
|
For = modelExpression,
|
||||||
|
};
|
||||||
|
var tagHelperContext = GetTagHelperContext();
|
||||||
|
var output = GetTagHelperOutput();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await tagHelper.ProcessAsync(tagHelperContext, output);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
view.Verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessAsync_SetsHtmlFieldPrefix_UsingModelExpression()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = "order.items[0].Property";
|
||||||
|
var bufferScope = new TestViewBufferScope();
|
||||||
|
var partialName = "_Partial";
|
||||||
|
var modelMetadataProvider = new TestModelMetadataProvider();
|
||||||
|
var containerModel = new TestModel { Property = new PropertyModel() };
|
||||||
|
var containerModelExplorer = modelMetadataProvider.GetModelExplorerForType(
|
||||||
|
typeof(TestModel),
|
||||||
|
containerModel);
|
||||||
|
var propertyModelExplorer = containerModelExplorer.GetExplorerForProperty(nameof(TestModel.Property));
|
||||||
|
|
||||||
|
var modelExpression = new ModelExpression("Property", propertyModelExplorer);
|
||||||
|
var viewContext = GetViewContext();
|
||||||
|
viewContext.ViewData.TemplateInfo.HtmlFieldPrefix = "order.items[0]";
|
||||||
|
|
||||||
|
var view = new Mock<IView>();
|
||||||
|
view.Setup(v => v.RenderAsync(It.IsAny<ViewContext>()))
|
||||||
|
.Callback((ViewContext v) =>
|
||||||
|
{
|
||||||
|
Assert.Equal(expected, v.ViewData.TemplateInfo.HtmlFieldPrefix);
|
||||||
|
})
|
||||||
|
.Returns(Task.CompletedTask)
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
var viewEngine = new Mock<ICompositeViewEngine>();
|
||||||
|
viewEngine.Setup(v => v.GetView(It.IsAny<string>(), partialName, false))
|
||||||
|
.Returns(ViewEngineResult.Found(partialName, view.Object));
|
||||||
|
|
||||||
|
var tagHelper = new PartialTagHelper(viewEngine.Object, bufferScope)
|
||||||
|
{
|
||||||
|
Name = partialName,
|
||||||
|
ViewContext = viewContext,
|
||||||
|
For = modelExpression,
|
||||||
|
};
|
||||||
|
var tagHelperContext = GetTagHelperContext();
|
||||||
|
var output = GetTagHelperOutput();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await tagHelper.ProcessAsync(tagHelperContext, output);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
view.Verify();
|
||||||
|
Assert.Equal("order.items[0]", viewContext.ViewData.TemplateInfo.HtmlFieldPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessAsync_UsesModelOnViewContextViewData_WhenModelExpresionIsNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var bufferScope = new TestViewBufferScope();
|
||||||
|
var partialName = "_Partial";
|
||||||
|
var model = new object();
|
||||||
|
var viewContext = GetViewContext();
|
||||||
|
viewContext.ViewData.Model = model;
|
||||||
|
|
||||||
|
var view = new Mock<IView>();
|
||||||
|
view.Setup(v => v.RenderAsync(It.IsAny<ViewContext>()))
|
||||||
|
.Callback((ViewContext v) =>
|
||||||
|
{
|
||||||
|
Assert.Same(model, v.ViewData.Model);
|
||||||
|
})
|
||||||
|
.Returns(Task.CompletedTask)
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
var viewEngine = new Mock<ICompositeViewEngine>();
|
||||||
|
viewEngine.Setup(v => v.GetView(It.IsAny<string>(), partialName, false))
|
||||||
|
.Returns(ViewEngineResult.Found(partialName, view.Object));
|
||||||
|
|
||||||
|
var tagHelper = new PartialTagHelper(viewEngine.Object, bufferScope)
|
||||||
|
{
|
||||||
|
Name = partialName,
|
||||||
|
ViewContext = viewContext,
|
||||||
|
};
|
||||||
|
var tagHelperContext = GetTagHelperContext();
|
||||||
|
var output = GetTagHelperOutput();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await tagHelper.ProcessAsync(tagHelperContext, output);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
view.Verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessAsync_DoesNotModifyHtmlFieldPrefix_WhenModelExpressionIsNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = "original";
|
||||||
|
var bufferScope = new TestViewBufferScope();
|
||||||
|
var partialName = "_Partial";
|
||||||
|
var model = new object();
|
||||||
|
var viewContext = GetViewContext();
|
||||||
|
viewContext.ViewData.Model = model;
|
||||||
|
viewContext.ViewData.TemplateInfo.HtmlFieldPrefix = expected;
|
||||||
|
|
||||||
|
var view = new Mock<IView>();
|
||||||
|
view.Setup(v => v.RenderAsync(It.IsAny<ViewContext>()))
|
||||||
|
.Callback((ViewContext v) =>
|
||||||
|
{
|
||||||
|
Assert.Equal(expected, v.ViewData.TemplateInfo.HtmlFieldPrefix);
|
||||||
|
})
|
||||||
|
.Returns(Task.CompletedTask)
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
var viewEngine = new Mock<ICompositeViewEngine>();
|
||||||
|
viewEngine.Setup(v => v.GetView(It.IsAny<string>(), partialName, false))
|
||||||
|
.Returns(ViewEngineResult.Found(partialName, view.Object));
|
||||||
|
|
||||||
|
var tagHelper = new PartialTagHelper(viewEngine.Object, bufferScope)
|
||||||
|
{
|
||||||
|
Name = partialName,
|
||||||
|
ViewContext = viewContext,
|
||||||
|
};
|
||||||
|
var tagHelperContext = GetTagHelperContext();
|
||||||
|
var output = GetTagHelperOutput();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await tagHelper.ProcessAsync(tagHelperContext, output);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
view.Verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessAsync_DisposesViewInstance()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var bufferScope = new TestViewBufferScope();
|
||||||
|
var partialName = "_Partial";
|
||||||
|
var viewContext = GetViewContext();
|
||||||
|
|
||||||
|
var disposable = new Mock<IDisposable>();
|
||||||
|
disposable.Setup(d => d.Dispose()).Verifiable();
|
||||||
|
var view = disposable.As<IView>();
|
||||||
|
|
||||||
|
view.Setup(v => v.RenderAsync(It.IsAny<ViewContext>()))
|
||||||
|
.Returns(Task.CompletedTask)
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
var viewEngine = new Mock<ICompositeViewEngine>();
|
||||||
|
viewEngine.Setup(v => v.GetView(It.IsAny<string>(), partialName, false))
|
||||||
|
.Returns(ViewEngineResult.Found(partialName, view.Object));
|
||||||
|
|
||||||
|
var tagHelper = new PartialTagHelper(viewEngine.Object, bufferScope)
|
||||||
|
{
|
||||||
|
Name = partialName,
|
||||||
|
ViewContext = viewContext,
|
||||||
|
};
|
||||||
|
var tagHelperContext = GetTagHelperContext();
|
||||||
|
var output = GetTagHelperOutput();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await tagHelper.ProcessAsync(tagHelperContext, output);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
disposable.Verify();
|
||||||
|
view.Verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessAsync_Throws_IfGetViewAndFindReturnNotFoundResults()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var bufferScope = new TestViewBufferScope();
|
||||||
|
var partialName = "_Partial";
|
||||||
|
var expected = string.Join(Environment.NewLine,
|
||||||
|
$"The partial view '{partialName}' was not found. The following locations were searched:",
|
||||||
|
"NotFound1",
|
||||||
|
"NotFound2",
|
||||||
|
"NotFound3",
|
||||||
|
"NotFound4");
|
||||||
|
var viewData = new ViewDataDictionary(new TestModelMetadataProvider(), new ModelStateDictionary());
|
||||||
|
var viewContext = GetViewContext();
|
||||||
|
|
||||||
|
var view = Mock.Of<IView>();
|
||||||
|
var viewEngine = new Mock<ICompositeViewEngine>();
|
||||||
|
viewEngine.Setup(v => v.GetView(It.IsAny<string>(), partialName, false))
|
||||||
|
.Returns(ViewEngineResult.NotFound(partialName, new[] { "NotFound1", "NotFound2" }));
|
||||||
|
|
||||||
|
viewEngine.Setup(v => v.FindView(viewContext, partialName, false))
|
||||||
|
.Returns(ViewEngineResult.NotFound(partialName, new[] { $"NotFound3", $"NotFound4" }));
|
||||||
|
|
||||||
|
var tagHelper = new PartialTagHelper(viewEngine.Object, bufferScope)
|
||||||
|
{
|
||||||
|
Name = partialName,
|
||||||
|
ViewContext = viewContext,
|
||||||
|
ViewData = viewData,
|
||||||
|
};
|
||||||
|
var tagHelperContext = GetTagHelperContext();
|
||||||
|
var output = GetTagHelperOutput();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||||
|
() => tagHelper.ProcessAsync(tagHelperContext, output));
|
||||||
|
Assert.Equal(expected, exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ViewContext GetViewContext()
|
||||||
|
{
|
||||||
|
return new ViewContext(
|
||||||
|
new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor()),
|
||||||
|
NullView.Instance,
|
||||||
|
new ViewDataDictionary(new TestModelMetadataProvider(), new ModelStateDictionary()),
|
||||||
|
Mock.Of<ITempDataDictionary>(),
|
||||||
|
TextWriter.Null,
|
||||||
|
new HtmlHelperOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TagHelperContext GetTagHelperContext()
|
||||||
|
{
|
||||||
|
return new TagHelperContext(
|
||||||
|
"partial",
|
||||||
|
new TagHelperAttributeList(),
|
||||||
|
new Dictionary<object, object>(),
|
||||||
|
Guid.NewGuid().ToString("N"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TagHelperOutput GetTagHelperOutput()
|
||||||
|
{
|
||||||
|
return new TagHelperOutput(
|
||||||
|
"partial",
|
||||||
|
new TagHelperAttributeList(),
|
||||||
|
(_, __) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestModel
|
||||||
|
{
|
||||||
|
public PropertyModel Property { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PropertyModel
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -108,6 +108,8 @@ namespace HtmlGenerationWebSite.Controllers
|
||||||
return View(_products);
|
return View(_products);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IActionResult ProductListUsingTagHelpers() => View(_products);
|
||||||
|
|
||||||
public IActionResult EmployeeList()
|
public IActionResult EmployeeList()
|
||||||
{
|
{
|
||||||
var employees = new List<Employee>
|
var employees = new List<Employee>
|
||||||
|
|
@ -165,6 +167,22 @@ namespace HtmlGenerationWebSite.Controllers
|
||||||
return View(warehouse);
|
return View(warehouse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IActionResult Warehouse()
|
||||||
|
{
|
||||||
|
var warehouse = new Warehouse
|
||||||
|
{
|
||||||
|
City = "City_1",
|
||||||
|
Employee = new Employee
|
||||||
|
{
|
||||||
|
Name = "EmployeeName_1",
|
||||||
|
OfficeNumber = "Number_1",
|
||||||
|
Address = "Address_1",
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return View(warehouse);
|
||||||
|
}
|
||||||
|
|
||||||
public IActionResult Environment()
|
public IActionResult Environment()
|
||||||
{
|
{
|
||||||
return View();
|
return View();
|
||||||
|
|
@ -209,5 +227,7 @@ namespace HtmlGenerationWebSite.Controllers
|
||||||
{
|
{
|
||||||
return View();
|
return View();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IActionResult PartialTagHelperWithoutModel() => View();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
|
PartialTagHelperWithoutModel: <partial name="../Shared/_Partial" />
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
@using HtmlGenerationWebSite.Models
|
||||||
|
@model IList<Product>
|
||||||
|
|
||||||
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form asp-action="Index" asp-controller="HtmlGeneration_Product" asp-antiforgery="true">
|
||||||
|
@for (var i = 0; i < Model.Count; i++)
|
||||||
|
{
|
||||||
|
<div>
|
||||||
|
<label asp-for="@Model[i].HomePage" class="product"></label>
|
||||||
|
<input asp-for="@Model[i].HomePage" type="url" size="50" disabled="disabled" readonly="readonly" />
|
||||||
|
</div>
|
||||||
|
<partial name="_ProductPartial" asp-for="@Model[i]" />
|
||||||
|
}
|
||||||
|
@* Print the HtmlFieldPrefix outside of the partial tag helper to ensure it hasn't been modified *@
|
||||||
|
<div>HtmlFieldPrefix = @ViewData.TemplateInfo.HtmlFieldPrefix</div>
|
||||||
|
<input type="submit" />
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
@model HtmlGenerationWebSite.Models.Warehouse
|
||||||
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
|
|
||||||
|
<h3>@Html.DisplayFor(m => m.City)</h3>
|
||||||
|
<partial name="_EmployeePartial" asp-for="Employee" />
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
@model HtmlGenerationWebSite.Models.Employee
|
||||||
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label asp-for="Name"></label>
|
||||||
|
<input asp-for="Name" type="text"/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label asp-for="OfficeNumber"></label>
|
||||||
|
<input asp-for="OfficeNumber" type="number"/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label asp-for="Address"></label>
|
||||||
|
<textarea asp-for="Address" rows="4" cols="50"></textarea>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Hello from partial
|
||||||
Loading…
Reference in New Issue