Add optional property to PartialTagHelper (#7991)
* Add optional to PartialTagHelper Addresses #7268
This commit is contained in:
parent
f6befb9ed3
commit
c2fcfabdf3
|
|
@ -22,6 +22,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
{
|
||||
private const string ForAttributeName = "for";
|
||||
private const string ModelAttributeName = "model";
|
||||
private const string OptionalAttributeName = "optional";
|
||||
private object _model;
|
||||
private bool _hasModel;
|
||||
private bool _hasFor;
|
||||
|
|
@ -74,6 +75,13 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When optional, executing the tag helper will no-op if the view cannot be located.
|
||||
/// Otherwise will throw stating the view could not be found.
|
||||
/// </summary>
|
||||
[HtmlAttributeName(OptionalAttributeName)]
|
||||
public bool Optional { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="ViewDataDictionary"/> to pass into the partial view.
|
||||
/// </summary>
|
||||
|
|
@ -96,16 +104,21 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var model = ResolveModel();
|
||||
var viewBuffer = new ViewBuffer(_viewBufferScope, Name, ViewBuffer.PartialViewPageSize);
|
||||
using (var writer = new ViewBufferTextWriter(viewBuffer, Encoding.UTF8))
|
||||
{
|
||||
await RenderPartialViewAsync(writer, model);
|
||||
var viewEngineResult = FindView();
|
||||
|
||||
// Reset the TagName. We don't want `partial` to render.
|
||||
output.TagName = null;
|
||||
output.Content.SetHtmlContent(viewBuffer);
|
||||
if (viewEngineResult.Success)
|
||||
{
|
||||
var model = ResolveModel();
|
||||
var viewBuffer = new ViewBuffer(_viewBufferScope, Name, ViewBuffer.PartialViewPageSize);
|
||||
using (var writer = new ViewBufferTextWriter(viewBuffer, Encoding.UTF8))
|
||||
{
|
||||
await RenderPartialViewAsync(writer, model, viewEngineResult.View);
|
||||
output.Content.SetHtmlContent(viewBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the TagName. We don't want `partial` to render.
|
||||
output.TagName = null;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
|
|
@ -139,7 +152,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
return ViewContext.ViewData.Model;
|
||||
}
|
||||
|
||||
private async Task RenderPartialViewAsync(TextWriter writer, object model)
|
||||
private ViewEngineResult FindView()
|
||||
{
|
||||
var viewEngineResult = _viewEngine.GetView(ViewContext.ExecutingFilePath, Name, isMainPage: false);
|
||||
var getViewLocations = viewEngineResult.SearchedLocations;
|
||||
|
|
@ -148,7 +161,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
viewEngineResult = _viewEngine.FindView(ViewContext, Name, isMainPage: false);
|
||||
}
|
||||
|
||||
if (!viewEngineResult.Success)
|
||||
if (!viewEngineResult.Success && !Optional)
|
||||
{
|
||||
var searchedLocations = Enumerable.Concat(getViewLocations, viewEngineResult.SearchedLocations);
|
||||
var locations = string.Empty;
|
||||
|
|
@ -161,7 +174,11 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
Resources.FormatViewEngine_PartialViewNotFound(Name, locations));
|
||||
}
|
||||
|
||||
var view = viewEngineResult.View;
|
||||
return viewEngineResult;
|
||||
}
|
||||
|
||||
private async Task RenderPartialViewAsync(TextWriter writer, object model, IView view)
|
||||
{
|
||||
// Determine which ViewData we should use to construct a new ViewData
|
||||
var baseViewData = ViewData ?? ViewContext.ViewData;
|
||||
var newViewData = new ViewDataDictionary<object>(baseViewData, model);
|
||||
|
|
|
|||
|
|
@ -569,7 +569,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProcessAsync_Throws_IfGetViewAndFindReturnNotFoundResults()
|
||||
public async Task ProcessAsync_Throws_If_NotOptional_And_GetViewAndFindReturnNotFoundResults()
|
||||
{
|
||||
// Arrange
|
||||
var bufferScope = new TestViewBufferScope();
|
||||
|
|
@ -596,6 +596,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
Name = partialName,
|
||||
ViewContext = viewContext,
|
||||
ViewData = viewData,
|
||||
Optional = false
|
||||
};
|
||||
var tagHelperContext = GetTagHelperContext();
|
||||
var output = GetTagHelperOutput();
|
||||
|
|
@ -605,6 +606,47 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
() => tagHelper.ProcessAsync(tagHelperContext, output));
|
||||
Assert.Equal(expected, exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProcessAsync_IfOptional_And_ViewIsNotFound_WillNotRenderAnything()
|
||||
{
|
||||
// Arrange
|
||||
var expected = string.Empty;
|
||||
var bufferScope = new TestViewBufferScope();
|
||||
var partialName = "_ThisViewDoesNotExists";
|
||||
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, searchedLocations: Array.Empty<string>()));
|
||||
viewEngine.Setup(v => v.FindView(viewContext, partialName, false))
|
||||
.Returns(ViewEngineResult.NotFound(partialName, searchedLocations: Array.Empty<string>()));
|
||||
|
||||
var tagHelper = new PartialTagHelper(viewEngine.Object, bufferScope)
|
||||
{
|
||||
Name = partialName,
|
||||
ViewContext = viewContext,
|
||||
Optional = true
|
||||
};
|
||||
var tagHelperContext = GetTagHelperContext();
|
||||
var output = GetTagHelperOutput();
|
||||
|
||||
// Act
|
||||
await tagHelper.ProcessAsync(tagHelperContext, output);
|
||||
|
||||
// Assert
|
||||
var content = HtmlContentUtilities.HtmlContentToString(output.Content, new HtmlTestEncoder());
|
||||
Assert.Empty(content);
|
||||
}
|
||||
|
||||
private static ViewContext GetViewContext()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue